First elements of list of list Prolog

Viewed 12610

I m studying Prolog and i see this code

foo([],[]).
foo([[A,_ ]|L], [A|P]) :-foo(L ,P).

The result say that this code take N element of list of list, Ad example if we give this query:

?foo([[car],[house],[man]],X)
X= [c,h,m]

At first read i see that something wrong. For me this code take the tail of list of list and the rest of first element of the list , so for me first expansion will be (trace)

foo([[house],[man]], ar)
foo([[man]], ouse)
foo([], an)
false.

I try to compile with swi-prolog and give this trace:

[trace]  ?- trace,foo([[car],[house],[man]],X).
Call: (9) foo([[car], [house], [man]], _1016) ? creep
Fail: (9) foo([[car], [house], [man]], _1016) ? creep
false.

What are I wrong?

1 Answers

Obtaining the first elements

The pattern [A, _] in your clause is wrong, or at at least not generic enough. [A, _] unifies with a list that contains exactly two elements, but this will thus fail for lists with more than two elements, or with one elements, like you found out.

You need to use the [A|_] pattern: indeed a list where the head is A, and we are not interested in the rest (tail). like:

foo([],[]).
foo([[A|_]|L], [A|P]) :- foo(L, P).

That being said, you can simplify this, by implementing a predicate that takes the head of a list:

head([H|_], H).

and then make use of maplist/3 [swi-doc]:

foo(A, B) :-
    maplist(head, A, B).

maplist will thus call head like head(Ai, Bi), with Ai and Bi elements of A and B respectively.

Obtaining a substring with the first character

but based on the sample output, this is not what you want: you also want to obtain the first "character" of the atom, we can do that by using string_chars/2 [swi-doc]:

head_first([A|_], C) :-
    string_chars(A, [C|_]).

and then define foo/2 again with maplist/3 [swi-doc]:

foo(A, B) :-
    maplist(head_first, A, B).

we then obtain:

?- foo([[car],[house],[man]], X).
X = [c, h, m].
Related