2

I'm trying to write a predicate to remove the head from every list in list of lists and add the tails to a new list. The resulting list should be returned as the second parameter.

Here's the attempt:

construct_new(S,New) :-
    New = [],
    new_situation(S,New).

new_situation([],_).
new_situation([H|T], New) :-
    chop(H, H1),
    new_situation(T, [H1|New]).

chop([_|T], T).

You would call it like this:

construct_new([[x,x],[b,c],[d,e,f]],S).

This, however, only produces output true..

tuulik
  • 75
  • 5
  • 2
    Think in terms of relations between lists. Take for example the clause `new_situation([], _).` This cannot be right: Do you really want this to hold for *any* second argument, such as for example `?- new_situation([], prolog).`, which currently succeeds? Since you are describing relations, ideally usable in all directions, avoid imperative names like "chop". Use more declarative names like `list_without_head/2` to describe the two arguments in a meaningful relational way. – mat Oct 02 '15 at 09:17
  • 4
    Can't help myself thinking that the predicate should be called `queen_of_hearts/2` ... – Paulo Moura Oct 02 '15 at 10:37
  • @PauloMoura that's brilliant (+1) :) – lurker Oct 02 '15 at 11:24

4 Answers4

3

Step-by-step execution

  1. Your query is construct_new(Input,Output), for some instanciated Input list.
  2. The first statement in construct_new/2 unifies Output (a.k.a. New) with the empty list. Where is the returned list supposed to be available for the caller? Both arguments are now unified.
  3. You call new_situation(Input,[])
  4. You match the second clause new_situation([H|T],[]), which performs its task recursively (step 4, ...), until ...
  5. You reach new_situation([],_), which successfully discards the intermediate list you built.

Solutions

  • Write a simple recursive predicate:

    new_situation([],[]).
    new_situation([[_|L]|T],[L|R]) :-
        new_situation(T,R).
    
  • Use maplist:

    construct_new(S,R) :-
        maplist(chop,S,R).
    

Remark

As pointed out by other answers and comments, your predicates are badly named. construct_new is not a relation, but an action, and could be used to represent almost anything. I tend to like chop because it clearly conveys the act of beheading, but this is not an appropriate name for a relation. repeat's list_head_tail(L,H,T) is declarative and associates variables to their roles. When using maplist, the other predicate (new_situation) doesn't even need to exist...

...even though guillotine/3 is tempting.

coredump
  • 37,664
  • 5
  • 43
  • 77
3

This could be done with a DCG:

owth(Lists, Tails) :-
    phrase(tails(Tails), Lists).

tails([]) --> [].
tails([T|Tails]) --> [[_|T]], tails(Tails).

Yielding these queries:

| ?- owth([[x,x],[b,c],[d,e,f]], T).

T = [[x],[c],[e,f]] ? ;

no
| ?- owth(L, [[x],[c],[e,f]]).

L = [[_,x],[_,c],[_,e,f]]

yes

(owth = Off with their heads! or, if used the other direction, On with their heads!)

If you also want to capture the heads, you can enhance it as follows:

owth(Lists, Heads, Tails) :-
    phrase(tails(Heads, Tails), Lists).

tails([], []) --> [].
tails([H|Hs], [T|Tails]) --> [[H|T]], tails(Hs, Tails).
lurker
  • 56,987
  • 9
  • 69
  • 103
2

We use maplist/[3-4] with one of these following auxiliary predicates:

list_tail([_|Xs],Xs).

list_head_tail([X|Xs],X,Xs).

Let's run some queries!

?- maplist(list_head_tail,[[x,x],[b,c],[d,e,f]],Heads,Tails).
Heads = [x,b,d],
Tails = [[x],[c],[e,f]].

If you are only interested in the tails, use maplist/4 together with list_head_tail/3 ...

?- maplist(list_head_tail,[[x,x],[b,c],[d,e,f]],_,Tails).
Tails = [[x],[c],[e,f]].

... or, even simpler, maplist/3 in tandem with list_tail/2:

?- maplist(list_tail,[[x,x],[b,c],[d,e,f]],Tails).
Tails = [[x],[c],[e,f]].
Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • 3
    What is wrong with the `chop/2` that OP already defined in their question? –  Oct 02 '15 at 09:49
  • 1
    @Boris. `chop/2` is a **bad** name. – repeat Oct 02 '15 at 09:57
  • For contrast: `chop(X, Y)` is true iff ... ??? (nobody knows from the name). `list_head_tail(Ls, H, Ts)` is true iff ... (it's easy to see what is intended from the name alone). – mat Oct 02 '15 at 09:58
  • So is `length/2` instead of `list_length/2`, and `is/2` should really have had the second argument as the output argument, and don't even get me started on `list_member` etc. :D But yes, it is a bad name. –  Oct 02 '15 at 09:59
  • 1
    @Boris. You're right. A lot of names have appeared and stayed during the past 40 years of Prolog history. Be that as it may, the time is always right to lead by example! – repeat Oct 02 '15 at 10:11
  • 1
    [slurp](https://clojuredocs.org/clojure.core/slurp) and [spit](https://clojuredocs.org/clojure.core/spit)? – coredump Oct 02 '15 at 10:55
  • 2
    @coredump or [library(filesex)](http://www.swi-prolog.org/pldoc/man?section=filesex)? –  Oct 02 '15 at 11:04
1

You can also use the somewhat ugly one-liner with findall/3:

?- L = [[x,x],[b,c],[d,e,f]],
   findall(T, ( member(M, L), append([_], T, M) ), R).
R = [[x], [c], [e, f]].

(OK, technically a two-liner. Either way, you don't even need to define a helper predicate.)

But definitely prefer the maplist solution that uses chop as shown above.

If you do the maplist expansion by hand, and name your chop/2 a bit better, you would get:

lists_tails([], []).
lists_tails([X|Xs], [T|Ts]) :-
    list_tail(X, T),
    lists_tails(Xs, Ts).

And since you can do unification in the head of the predicate, you can transform this to:

lists_tails([], []).
lists_tails([[_|T]|Xs], [T|Ts]) :-
    lists_tails(Xs, Ts).

But this is identical to what you have in the other answer.

Exercise: why can't we say:

?- maplist(append([_]), R, [[x,x],[b,c],[d,e,f]]).
Community
  • 1
  • 1
  • 1
    The code based on `findall/3` breaks down when used with non-ground terms. The query `?- L = [[A,B],[C,D,E]], findall(T, ( member(M, L), append([_], T, M) ), R).` gives as answer `L = [[A, B], [C, D, E]], R = [[_G220], [_G211, _G214]].` – repeat Oct 02 '15 at 10:05