这是一个非常常见的问题,但我想把它与两个在我眼中看起来非常相似的例子联系起来,而且还是一个正确而另一个没有。
正确的例子:
k_th_element(X,[X|_],1).
k_th_element(X,[_|L],K):- K>1,K1 is (K-1),k_th_element(X,L,K1).
错误的例子
length2(1,[_]).
length2(X,[_|Ys]) :- X>1, X1 is (X-1), length(X1,Ys).
为什么prolog会对每个案件抱怨或不抱怨?
更新:我想我明白了。我无法理解的是,谓词是什么,但你如何称呼它并不重要。所以这是正确的:k_th_element(X,[1,2,3,4,5],3)因为你有一个K的值,它是“是”运算符的正确变量。但同时k_th_element(3,[1,2,3,4,5],Y)将不起作用,因为Y是一个变量,我们的“目标”,我们不能在右边“是“运营商。如我错了请纠正我。
如同垫子所提出的,有一种更灵活的方式来实现相同的目标:
:- use_module(library(clpfd)).
length2(0,[]).
length2(X,[_|Ys]) :- X#>0, X1#=X-1, length2(X1,Ys).
首先,有参数顺序。对于length/2
来说,它更像是length(List, Length)
。
对于给定列表和未知长度的情况,由于所有X1 #= X-1
约束暗示了N
约束变量,因此您的版本效率相对较低。版本length3/2
有一个约束变量。 (它快了大约7倍。我仍然感到惊讶的是它并不比它快,也许有人可以帮助另一个答案?)
:- use_module(library(clpfd)).
length2([], 0).
length2([_E|Es], N0) :-
N0 #> 0,
N1 #= N0-1,
length2(Es, N1).
length3(Es, N) :-
length3(Es, 0, N).
length3([], N,N).
length3([_E|Es], N0,N) :-
N1 is N0+1,
N #>= N1,
length3(Es, N1,N).
/*
?- length(L,1000), time(length2(L,N)).
% 783,606 inferences, 0.336 CPU in 0.347 seconds (97% CPU, 2332281 Lips)
L = [_G23, _G26, _G29, _G32, _G35, _G38, _G41, _G44, _G47|...],
N = 1000.
?- length(L,1000), time(length3(L,N)).
% 127,006 inferences, 0.047 CPU in 0.058 seconds (81% CPU, 2719603 Lips)
L = [_G23, _G26, _G29, _G32, _G35, _G38, _G41, _G44, _G47|...],
N = 1000.
*/
使用reflection predicates可以构建以下list_length/2
变体:
:- use_module(library(clpfd)).
list_length(Es, N) :-
( fd_sup(N, Ub), integer(Ub)
-> ( length(Es, M),
( M >= Ub
-> !,
M == Ub
; true
),
M = N
)
; length(Es, N)
).
上面的实现结合了两个不错的属性:
list_length(Xs,N)
具有有限的上界时,N
普遍终止。
使用SWI-Prolog 8.0.0:
? - N in 1..3,list_length(Xs,N)。 N = 1,Xs = [_ A]; N = 2,Xs = [_ A,_ B]; N = 3,Xs = [_ A,_B,_C]。 %普遍终止length/2
来最小化辅助计算(从而最小化运行时)。
让我们将list_length/2
的运行时间与length3/2
中提供的this earlier answer进行比较!
使用SWI-Prolog 8.0.0和命令行选项-O
:
? - 时间((N in 1..100000,list_length(_,N),false; true))。 %2,700,130次推断,0.561秒内0.561 CPU(100%CPU,4812660 Lips)为真。 ? - 时间((N in 1..100000,length3(_,N),false; true))。 %14,700,041推断,3.948 CPU,3.949秒(100%CPU,3723234 Lips)true。请注意,上述内容也适用于SICStus Prolog:SWI的fd_sup/2
在SICStus中称为fd_max/2
。