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 Haskell 1 . 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). % trouble: goal does not terminate ?- take([,_], 1, [x]). true. % trouble: variable 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], , Ls). ?- take(Es, 0, Xs). ?- take([A,_], 1, [x]).
Safer now!
The obvious solution would be:
take(List, N, Prefix) :-
length(List, Len),
( Len =< N
-> Prefix = List
; length(Prefix, N),
append(Prefix, _, List)
).
Less thinking means less opportunity for mistakes. It also makes the predicate more general.
your base case is fine
take([H|T], 0, []).
And also you can say what if N is 1
take([H|T],1,[H]).
But you recursive case some variable is not defined like L2. So we can write this as
take([X|T1],N,[X|T2]):-
N>=0,
N1 is N-1,
take(T1,N1,T2).
which case all varibles are pattern-matched.
取(L,N,L1):-长度(L1,N),追加(L1,_,L)。
This is performant, general and deterministic:
first_elements_of_list(IntElems, LongLst, ShortLst) :-
LongLst = [H|T],
( nonvar(IntElems) -> Once = true
; is_list(ShortLst) -> Once = true
; Once = false
),
first_elements_of_list_(T, H, 1, IntElems, ShortLst),
(Once = true -> ! ; true).
first_elements_of_list_([], H, I, I, [H]).
first_elements_of_list_([_|_], H, I, I, [H]).
first_elements_of_list_([H|LongLst], PrevH, Upto, IntElems, [PrevH|ShortLst]) :-
Upto1 is Upto + 1,
first_elements_of_list_(LongLst, H, Upto1, IntElems, ShortLst).
Result in swi-prolog:
?- first_elements_of_list(N, [a, b, c], S).
N = 1,
S = [a] ;
N = 2,
S = [a,b] ;
N = 3,
S = [a,b,c].
?- first_elements_of_list(2, [a, b, c], S).
S = [a,b].
Below is a variant which also supports:
?- first_elements_of_list_more(10, [5, 1, 2, 7], L1).
L1 = [5,1,2,7].
first_elements_of_list_more(IntElems, [H|LongLst], [H|ShortLst]) :-
once_if_nonvar(IntElems, first_elements_of_list_more_(LongLst, 1, IntElems, ShortLst)).
first_elements_of_list_more_([], Inc, Elems, []) :-
(var(Elems) -> Inc = Elems
; Elems >= Inc).
first_elements_of_list_more_([_|_], E, E, []).
first_elements_of_list_more_([H|LongLst], Upto, IntElems, [H|ShortLst]) :-
succ(Upto, Upto1),
first_elements_of_list_more_(LongLst, Upto1, IntElems, ShortLst).
once_if_nonvar(Var, Expr) :-
nonvar(Var, Bool),
call(Expr),
(Bool == true -> ! ; true).
nonvar(Var, Bool) :-
(nonvar(Var) -> Bool = true ; Bool = false).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.