我需要编写一个Prolog谓词take(L, N, L1)
,如果列表L1
包含列表N
的第一个L
元素,则会以相同的顺序成功。例如:
?- take([5,1,2,7], 3, L1).
L1 = [5,1,2]
?- take([5,1,2,7], 10, L1).
L1 = [5,1,2,7]
到目前为止,Prolog对我来说没什么意义,而且我很难将其分解。以下是我到目前为止的情况:
take([H|T], 0, []).
take([H|T], N, L1) :-
take(T, X, L2),
X is N-1.
你能解释一下我在这里做错了吗?
答案 0 :(得分:7)
这是一个在Haskell 1 等函数式语言中实现take
的关系对应关系的定义。首先,参数顺序应该是不同的,这有利于部分应用。有一个剪切,但只有在内置(=<)/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] ? ;
no
注意上面的查询如何读取:
如何从
Xs
获取Ys
中的2个元素?
有3种不同的答案。如果Xs
为空,那么Ys
也是如此。如果Xs
是包含一个元素的列表,那么Ys
也是如此。如果Xs
至少包含2个元素,则这两个元素为Ys
。
1)唯一的区别是take(-1, Xs,Ys)
失败(对于所有Xs, Ys
)。最好的方法是发出与domain_error
类似的arg(-1,s(1),2)
功能
答案 1 :(得分:3)
findall / 3它有点像瑞士刀&#39; Prolog。我会用这个片段:
take(Src,N,L) :- findall(E, (nth1(I,Src,E), I =< N), L).
答案 2 :(得分:3)
如果实例化正确,则above code by @CapelliC可以工作;如果没有,它可能表现出不稳定的行为:
?- take(Es, 0, Xs). **LOOPS** % trouble: goal does not terminate ?- take([A,_], 1, [x]). true. % trouble: variable A remains unbound
为防止这种情况,您可以使用
iwhen/2
喜欢这样:
take(Src, N, L) :-
iwhen(ground(N+Src), findall(E, (nth1(I,Src,E), I =< N), L)).
使用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
现在更安全!
答案 3 :(得分:1)
显而易见的解决方案是:
take(List, N, Prefix) :-
length(List, Len),
( Len =< N
-> Prefix = List
; length(Prefix, N),
append(Prefix, _, List)
).
少思考意味着错误的机会减少。它也使谓词更加通用。
答案 4 :(得分:1)
您的基本情况很好
take([H|T], 0, []).
如果N为1,你也可以说
take([H|T],1,[H]).
但是在递归的情况下,未定义某些变量,例如L2。所以我们可以这样写
take([X|T1],N,[X|T2]):-
N>=0,
N1 is N-1,
take(T1,N1,T2).
在所有情况下,所有变量都是模式匹配的。
答案 5 :(得分:0)
take(L,N,L1):-长度(L1,N),append(L1,_,L)。