1

Here is my implementation of flatten/2:

flt([], []).
flt([H|L], [H|X]):-
    not(is_list(H)),
    flt(L, X).
flt([H|L], X):-
    append(R, F, X),
    flt(H, R),
    flt(L, F).

The expected result is given:

?- flt([1,[2,3,[4,5],6],7], X).
X = [1, 2, 3, 4, 5, 6, 7]

However, when I hit ; the stack limit is exceeded. Why does this happen? Where is the infinite recursion?

Thanks.

1 Answer 1

2

The problem is that you call append(R, F, X) with only uninstantiated variables.

There is only one solution for flt(+List, -Flatlist). However, on backtracking, append(R, F, X) is tried again and again and again without ever finding another solution...

You can test this by asking

?- append(R, F, X).
R = [],
F = X ;
R = [_948],
X = [_948|F] ;
R = [_948, _960],
X = [_948, _960|F] ;
R = [_948, _960, _972],
X = [_948, _960, _972|F] ;
R = [_948, _960, _972, _984],
X = [_948, _960, _972, _984|F] ;
R = [_948, _960, _972, _984, _996],
X = [_948, _960, _972, _984, _996|F] 
...

The solution is to rearrange the goals in your third clause:

flt([H|L], X):-
    flt(H, R),
    flt(L, F),
    append(R, F, X).

This is a very good example for the fact that Prolog is not purely declarative, since its implementation of resolution by simple chronological backtracking forces procedural features on the language.


To illustrate the problem, have a look at this trace (where I skipped a lot in order to just emphasize the loop problem):

[trace]  ?- flt([[a]],X).
   Call: (8) flt([[a]], _680) ? creep
^  Call: (9) not(is_list([a])) ? creep
^  Fail: (9) not(user:is_list([a])) ? creep
   Redo: (8) flt([[a]], _680) ? creep
   Call: (9) lists:append(_904, _906, _680) ? creep
   Exit: (9) lists:append([], _680, _680) ? creep
   Call: (9) flt([a], []) ? skip
   Fail: (9) flt([a], []) ? creep
   Redo: (9) lists:append(_904, _906, _680) ? creep
   Exit: (9) lists:append([_890], _898, [_890|_898]) ? creep
   Call: (9) flt([a], [_890]) ? skip
   Exit: (9) flt([a], [a]) ? creep
   Call: (9) flt([], _898) ? skip
   Exit: (9) flt([], []) ? creep
   Exit: (8) flt([[a]], [a]) ? creep
X = [a] ;
   Redo: (9) flt([a], [_890]) ? skip
   Fail: (9) flt([a], [_890]) ? creep
   Redo: (9) lists:append([_890|_892], _918, [_890|_898]) ? creep
   Exit: (9) lists:append([_890, _902], _910, [_890, _902|_910]) ? creep
   Call: (9) flt([a], [_890, _902]) ? skip
   Fail: (9) flt([a], [_890, _902]) ? creep
   Redo: (9) lists:append([_890, _902|_904], _930, [_890, _902|_910]) ? creep
   Exit: (9) lists:append([_890, _902, _914], _922, [_890, _902, _914|_922]) ? creep
   Call: (9) flt([a], [_890, _902, _914]) ? skip
   Fail: (9) flt([a], [_890, _902, _914]) ? creep
   Redo: (9) lists:append([_890, _902, _914|_916], _942, [_890, _902, _914|_922]) ? creep
   Exit: (9) lists:append([_890, _902, _914, _926], _934, [_890, _902, _914, _926|_934]) ? creep
   Call: (9) flt([a], [_890, _902, _914, _926]) ? skip
   Fail: (9) flt([a], [_890, _902, _914, _926]) ? creep
   Redo: (9) lists:append([_890, _902, _914, _926|_928], _954, [_890, _902, _914, _926|_934]) ? creep
   Exit: (9) lists:append([_890, _902, _914, _926, _938], _946, [_890, _902, _914, _926, _938|_946]) ? creep
   Call: (9) flt([a], [_890, _902, _914, _926, _938]) ? skip
   Fail: (9) flt([a], [_890, _902, _914, _926, _938]) ? creep
   Redo: (9) lists:append([_890, _902, _914, _926, _938|_940], _966, [_890, _902, _914, _926, _938|_946]) ? creep
   Exit: (9) lists:append([_890, _902, _914, _926, _938, _950], _958, [_890, _902, _914, _926, _938, _950|_958]) ? creep
   Call: (9) flt([a], [_890, _902, _914, _926, _938, _950]) ? skip
   Fail: (9) flt([a], [_890, _902, _914, _926, _938, _950]) ? creep
   Redo: (9) lists:append([_890, _902, _914, _926, _938, _950|_952], _978, [_890, _902, _914, _926, _938, _950|_958]) ? 

This drawing shows it graphically. What happens after asking for an additional answer is colored red.

.


Another possible solution is to add cuts:

flt([], []).
flt([H|L], [H|X]):-
    not(is_list(H)),
    flt(L, X),
    !.

flt([H|L], X):-
    append(R, F, X),
    flt(H, R),
    !,
    flt(L, F).
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.