Prologue: take the first "N" elements of the list

I need to write a Prolog predicate take(L, N, L1)

that succeeds if the list L1

contains the first N

elements of the list L

in the same order. For example:

?- take([5,1,2,7], 3, L1).
L1 = [5,1,2]
?- take([5,1,2,7], 10, L1).
L1 = [5,1,2,7] 

      

The prologue still doesn't make any sense to me and it's hard for me to break it. Here's what I have so far:

take([H|T], 0, []).
take([H|T], N, L1) :-
   take(T, X, L2),
   X is N-1.

      

Could you please explain what I did wrong here?

+5


source to share


4 answers


Here is a definition that implements the relational counterpart take

in functional languages ​​like Haskell 1 . First, the order of the arguments must be different, which makes partial application easier. There is a cut, but only after checking for an error inline (=<)/2

that throws instantiation_error

if the argument contains a variable.

take(N, _, Xs) :- N =< 0, !, N =:= 0, Xs = [].
take(_, [], []).
take(N, [X|Xs], [X|Ys]) :- M is N-1, take(M, Xs, Ys).


| ?- take(2, Xs, Ys).
Xs = [],
Ys = [] ? ;
Xs = [_A],
Ys = [_A] ? ;
Xs = [_A,_B|_C],
Ys = [_A,_B] ? ;
no

      

Notice how the above query is read:

How to take 2 elements from Xs

to get Ys

?



And there are 3 different answers. If Xs

empty, then and Ys

. If Xs

is a list with one element, then and Ys

. If it Xs

has at least 2 elements, then these two are Ys

.


1) The only difference is that it take(-1, Xs,Ys)

fails (for everyone Xs, Ys

). Probably best would be to release something domain_error

likearg(-1,s(1),2)

+7


source


findall / 3 is a bit of the "Swiss knife" of Prologue. I would use this snippet:



take(Src,N,L) :- findall(E, (nth1(I,Src,E), I =< N), L).

      

+3


source


The @CapelliC code above works if the instantiation is correct ; if not, it might show erroneous behavior:

? - take (Es, 0, Xs).
** LOOPS **                    % trouble: goal does not terminate

? - take ([ A , _], 1, [x]).          
true. % trouble: variable A remains unbound

To protect against this, you can use as follows: iwhen/2

take(Src, N, L) :-
   iwhen(ground(N+Src), findall(E, (nth1(I,Src,E), I =< N), L)).

      

Examples of queries are performed with SWI-Prolog 8.0.0:

? - take ([a, b, c, d, e, f], 3, Ls).
Ls = [a, b, c].

? - take ([a, b, c, d, e, f], N , Ls).
ERROR: Arguments are not sufficiently instantiated

? - take (Es, 0, Xs).
ERROR: Arguments are not sufficiently instantiated

? - take ([A, _], 1, [x]).
ERROR: Arguments are not sufficiently instantiated

Safer now!

+3


source


The obvious solution would be this:

take(List, N, Prefix) :-
    length(List, Len),
    (   Len =< N
    ->  Prefix = List
    ;   length(Prefix, N),
        append(Prefix, _, List)
    ).

      

Less thinking means less room for error. It also makes the predicate more general.

+1


source







All Articles