[英]Prolog: Take the first "N" elements of a list
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.我需要编写一个 Prolog 谓词
take(L, N, L1)
如果列表L1
以相同的顺序包含列表L
的前N
个元素,它就会成功。 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.到目前为止,Prolog 对我来说意义不大,而且我很难分解它。 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 .这是一个实现关系对应物的定义,以
take
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.有一个切入点,但只有在错误检查内置
(=<)/2
,如果参数包含变量,它会产生一个instantiation_error
。
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
?如何从
Xs
取出 2 个元素来获得Ys
?
And there are 3 different answers.并且有3个不同的答案。 If
Xs
is empty, then so is Ys
.如果
Xs
是空的,那么Ys
也是空的。 If Xs
is a list with one element, then so is Ys
.如果
Xs
是一个包含一个元素的列表,那么Ys
。 If Xs
has at least 2 elements, then those two are Ys
.如果
Xs
至少有 2 个元素,那么这两个元素就是Ys
。
1) The only difference being that take(-1, Xs,Ys)
fails (for all Xs, Ys
). 1) 唯一的区别是
take(-1, Xs,Ys)
失败(对于所有Xs, Ys
)。 Probably the best would be to issue a domain_error
similar to arg(-1,s(1),2)
可能最好的方法是发出类似于
arg(-1,s(1),2)
的domain_error
findall/3 it's a bit the 'swiss knife' of Prolog. findall/3 有点像 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;如果实例化正确,@CapelliC的代码就可以工作; 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:为了防止这种情况,您可以像这样使用
iwhen/2
:
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:使用 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!现在更安全!
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你也可以说如果 N 是 1
take([H|T],1,[H]).
But you recursive case some variable is not defined like L2.但是在递归情况下,某些变量没有像 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: 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).
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.