I need to write a Prolog predicate take(L, N, L1)
which succeeds if list L1
contains the first N
elements of 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]
Prolog thus far is making little sense to me, and I'm having a hard time breaking it down. Here is what I have so far:
take([H|T], 0, []).
take([H|T], N, L1) :-
take(T, X, L2),
X is N-1.
Can you please explain what I did wrong here?
Here is a definition that implements the relational counterpart to take
in functional languages like Haskell1. First, the argument order should be different which facilitates partial application. There is a cut, but only after the error checking built-in (=<)/2
which produces an instantiation_error
should the argument contain 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].
Note how above query reads:
How can one take 2 elements from
Xs
to getYs
?
And there are 3 different answers. If Xs
is empty, then so is Ys
. If Xs
is a list with one element, then so is Ys
. If Xs
has at least 2 elements, then those two are Ys
.
1) The only difference being that take(-1, Xs,Ys)
fails (for all Xs, Ys
). Probably the best would be to issue a domain_error
similar to arg(-1,s(1),2)
findall/3 it's a bit the 'swiss knife' of Prolog. I would use this snippet:
take(Src,N,L) :- findall(E, (nth1(I,Src,E), I =< N), L).
The code by @CapelliC works if the instantiation is right; if not, it can show erratic behavior:
?- take(Es, 0, Xs). **LOOPS** % trouble: goal does not terminate ?- take([A,_], 1, [x]). true. % trouble: variable A remains unbound
To safeguard against this you can use
iwhen/2
like so:
take(Src, N, L) :-
iwhen(ground(N+Src), findall(E, (nth1(I,Src,E), I =< N), L)).
Sample queries run 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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With