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?
The above code by @CapelliC works if the instantiation is right; if not, it can show erratic behavior:
To safeguard against this you can use
iwhen/2
like so:Sample queries run with SWI-Prolog 8.0.0:
Safer now!
The obvious solution would be:
Less thinking means less opportunity for mistakes. It also makes the predicate more general.
your base case is fine
And also you can say what if N is 1
But you recursive case some variable is not defined like L2. So we can write this as
which case all varibles are pattern-matched.
findall/3 it's a bit the 'swiss knife' of Prolog. I would use this snippet:
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 aninstantiation_error
should the argument contain a variable.Note how above query reads:
And there are 3 different answers. If
Xs
is empty, then so isYs
. IfXs
is a list with one element, then so isYs
. IfXs
has at least 2 elements, then those two areYs
.1) The only difference being that
take(-1, Xs,Ys)
fails (for allXs, Ys
). Probably the best would be to issue adomain_error
similar toarg(-1,s(1),2)