Search code examples
listprologflattendcgdifference-lists

Flatten a list in Prolog


I've only been working with Prolog for a couple days. I understand some things but this is really confusing me.

I'm suppose to write a function that takes a list and flattens it.

?- flatten([a,[b,c],[[d],[],[e]]],Xs).  
Xs = [a,b,c,d,e].                           % expected result

The function takes out the inner structures of the list.

This is what I have so far:

flatten2([],[]).
flatten2([Atom|ListTail],[Atom|RetList]) :-
      atom(Atom), flatten2(ListTail,RetList).
flatten2([List|ListTail],RetList) :-
      flatten2(List,RetList).

Now, this works when I call:

?- flatten2([a,[b,c],[[d],[],[e]]], R).
R = [a,b,c,d,e].                         % works as expected!

But when I call to see if a list that I input is already flattened, is returns false instead of true:

?- flatten2([a,[b,c],[[d],[],[e]]], [a,b,c,d,e]).
false.                                   % BAD result!

Why does it work on one hand, but not the other? I feel like I'm missing something very simple.


Solution

  • The definition of flatten2/2 you've given is busted; it actually behaves like this:

    ?- flatten2([a, [b,c], [[d],[],[e]]], R).
    R = [a, b, c] ;
    false. 
    

    So, given the case where you've already bound R to [a,b,c,d,e], the failure isn't surprising.

    Your definition is throwing away the tail of lists (ListTail) in the 3rd clause - this needs to be tidied up and connected back into the list to return via RetList. Here is a suggestion:

    flatten2([], []) :- !.
    flatten2([L|Ls], FlatL) :-
        !,
        flatten2(L, NewL),
        flatten2(Ls, NewLs),
        append(NewL, NewLs, FlatL).
    flatten2(L, [L]).
    

    This one recursively reduces all lists of lists into either single item lists [x], or empty lists [] which it throws away. Then, it accumulates and appends them all into one list again out the output.

    Note that, in most Prolog implementations, the empty list [] is an atom and a list, so the call to atom([]) and is_list([]) both evaluate to true; this won't help you throw away empty lists as opposed to character atoms.