我做了一个Prolog谓词posAt(List1,P,List2)
来测试P
和List1
的{{1}}位置的元素是否相等:
List2
测试时:
posAt([X|Z], 1, [Y|W]) :-
X = Y.
posAt([Z|X], K, [W|Y]) :-
K > 1,
Kr is K - 1,
posAt(X, Kr, Y).
我期望输出?- posAt([1,2,3], X, [a,2,b]).
,但我得到以下错误:
X = 2
为什么我收到此错误?
答案 0 :(得分:9)
Prolog谓词是参数之间的关系和你的陈述
List1和List2的P位置的元素相等
显然是可以使用多种解决方案的示例。
?- posAt([1,2,3],X,[1,5,3,7]).
X = 1.
因此sharky的答案清楚地解释了为什么出现技术错误,需要进行小的修正:
posAt([X0|_], Pos, Pos, [X1|_]) :-
X0 == X1.
现在它按预期工作。
?- posAt([1,2,3],X,[1,5,3,7]).
X = 1 ;
X = 3 ;
false.
为列表处理编写简单的谓词,这是一个非常有价值的学徒实践,也是有效学习语言的主要方式。如果您还倾向于研究可用的库谓词,那么这是一个使用库中的nth1 / 3的版本(lists)
posAt(L0, P, L1) :-
nth1(P, L0, E),
nth1(P, L1, E).
输出:
?- posAt([1,2,3],X,[1,5,3,7]).
X = 1 ;
X = 3.
尝试理解为什么在这种情况下SWI-Prolog'顶级'解释器能够推断解决方案的确定性可能会很有趣。
答案 1 :(得分:7)
这是因为,当Prolog评估子目标K > 1
时,K
仍然是未绑定变量而不是数字。标准Prolog不能(不会)评估数值范围限制的真/假值,例如当它们不被研磨时(与CLP这样的约束求解器相反,它允许这种情况,但工作方式不同)。
考虑这个解决方案:
posAt(L0, Pos, L1) :-
posAt(L0, 1, Pos, L1).
posAt([X0|_], Pos, Pos, [X1|_]) :-
X0 == X1.
posAt([_|X0s], CurrPos, Pos, [_|X1s]) :-
NextPos is CurrPos + 1,
posAt(X0s, NextPos, Pos, X1s).
第一个谓词posAt/3
设置初始条件:列出为位置1,并调用posAt/4
来迭代列表。
posAt/4
的第一个子句是匹配条件:同一位置的两个列表中的元素相等。在这种情况下,当前位置变量与结果Pos
统一。
如果上述子句失败,因为列表元素X0
和X1
不相等,则列表位置CurrPos
加1,并且对posAt/4
的递归调用开始再次处理下一对项目。
编辑:删除posAt/4
第一个条款中的错误剪切(感谢@chac提取)
答案 2 :(得分:2)
此类问题的一般解决方案是约束。
使用clpfd进行适用于所有方向的整数运算:
:- use_module(library(clpfd)).
posAt([X|_], 1, [X|_]).
posAt([_|X], K, [_|Y]) :-
K #> 1,
Kr #= K - 1,
posAt(X,Kr,Y).
通过这些简单的更改,您的示例完全按预期工作:
?- posAt([1,2,3], X, [a,2,b]). X = 2 ; false.
答案 3 :(得分:2)
(这只是在我的仪表板上弹出,因此迟到的答案......)
我查看了这个问题,并在考虑是否有可能提供接近原始问题的解决方案。正如已经解释的那样,问题是关系>
需要实例化其参数。实际上is
类似。但是,通过重新排序目标可以很容易地解决这个问题:
posAt([X|_], 1, [X|_]).
posAt([_|X], K, [_|Y]) :-
posAt(X, Kr, Y),
K is Kr+1,
K > 1.
这个解决方案是,只要K
被接地,运行时在两个列表的长度上都是线性的。但是,如果第一个元素在列表中匹配,则会出现问题,如重复答案中所示。
事实上,最后一个元素是多余的,因此是等价的。
posAt([X|_], 1, [X|_]).
posAt([_|X], K, [_|Y]) :-
posAt(X, Kr, Y),
K is Kr+1.
然而,正如@repeat证明的那样,这段代码非常慢。这可能是因为代码破坏了尾递归。
逻辑清洁解决方案可以解决此问题。在这里,我们将使用Peano Axiomes(s / 1后继者或关系)代表自然数字,解决方案将成为
posAt([X|_], zero, [X|_]).
posAt([_|X], s(K), [_|Y]) :-
posAt(X, K, Y).
然而,这很难及时,因此, hackish 解决方案大致相当于
posAt([X|_], [a], [X|_]).
posAt([_|X], [a|L], [_|Y]) :-
posAt(X, L, Y).
此解决方案的时间安排
N=8000,numlist(1,N,_Zs), length(_LN,N),time(posAt(_Zs,_LN,_Zs)).
7,999 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 7342035 Lips)
N = 8000
答案 4 :(得分:2)
TL; DR: @CAFEBABE,@CapelliC,@mat和@sharky 全部的答案落空短!
那么...... 完全是前面提出的答案的缺点?
@ CAFEBABE'陈述:
这个解决方案的好处是运行时在两个列表的长度上是线性的。
让我们把这个陈述付诸实践!
?- numlist(1,1000,Zs), time(posAt__CAFEBABE1(Zs,1000,Zs)). % 999,001 inferences, 0.090 CPU in 0.091 seconds (100% CPU, 11066910 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 4 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 66738 Lips) false.
无赖!其他人做得很好:
?- numlist(1,1000,Zs), time(posAt__CapelliC1(Zs,1000,Zs)). % 671 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 3492100 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...]. ?- numlist(1,1000,Zs), time(posAt__mat1(Zs,1000,Zs)). % 3,996 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 3619841 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 5 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 154703 Lips) false. ?- numlist(1,1000,Zs), time(posAt__sharky1(Zs,1000,Zs)). % 1,000 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 2627562 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 4 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 97109 Lips) false.
@CapelliC使用nth1/3
,这可能(并确实)导致通用终止问题:
?- time((posAt__CapelliC1(_,N,[a,b,c]), false)). **LOOPS**
d'哦!其他人都做得很好:
?- time((posAt__CAFEBABE1(_,_,[a,b,c]), false)). % 14 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 1098470 Lips) false. ?- time((posAt__mat1(_,_,[a,b,c]), false)). % 2,364 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 2764075 Lips) false. ?- time((posAt__sharky1(_,_,[a,b,c]), false)). % 6 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 207247 Lips) false.
@ mat的代码存在复杂性问题。
@CAFEBABE和@CapelliC做"好一点" - 他们的代码更快,因为他们使用依赖于较低级别的原语(is)/2
和nth1/3
。 / p>
?- numlist(1,1000,Zs), time((posAt__mat1(Zs,_,_), false)). % 33,365,972 inferences, 1.643 CPU in 1.643 seconds (100% CPU, 20304661 Lips) false. ?- numlist(1,1000,Zs), time((posAt__CAFEBABE1(Zs,_,_), false)). % 1,001,002 inferences, 0.083 CPU in 0.083 seconds (100% CPU, 12006557 Lips) false. ?- numlist(1,1000,Zs), time((posAt__CapelliC1(Zs,_,_), false)). % 171,673 inferences, 0.030 CPU in 0.030 seconds (100% CPU, 5810159 Lips) false.
@sharky的代码在这方面显然是最好的:
?- numlist(1,1000,Zs), time((posAt__sharky1(Zs,_,_), false)). % 1,003 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 1605658 Lips) false.
@ sharky的代码使用元逻辑内置谓词(==)/2
,这使得它在使用不充分实例化的术语时会失去逻辑健全性。此查询应该成功:
?- posAt__sharky1([a], 1, Xs). false.
其他代码都给出了一个逻辑上合理的答案:
?- posAt__CAFEBABE1([a], 1, Xs). Xs = [a|_G235] ; false. ?- posAt__CapelliC1([a], 1, Xs). Xs = [a|_G235]. ?- posAt__mat1([a], 1, Xs). Xs = [a|_G235] ; false.
过去第一个答案,@ CAFEBABE的代码多一点效率低下:
?- numlist(1,1000,Zs), time(posAt__CAFEBABE1(Zs,1,Zs)). % 0 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 0 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 999,004 inferences, 0.076 CPU in 0.076 seconds (100% CPU, 13121058 Lips) false.
与@ sharky的代码相似 - 但是要小一个数量级的问题:
?- numlist(1,1000,Zs), time(posAt__sharky1(Zs,1,Zs)). % 1 inferences, 0.000 CPU in 0.000 seconds (75% CPU, 31492 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 1,003 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 1078556 Lips) false.
@CapelliC和@mat的代码都做得很好:
?- numlist(1,1000,Zs), time(posAt__CapelliC1(Zs,1,Zs)). % 7 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 306802 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...]. ?- numlist(1,1000,Zs), time(posAt__mat1(Zs,1,Zs)). % 0 inferences, 0.000 CPU in 0.000 seconds (80% CPU, 0 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 5 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 345662 Lips) false.
那么......我们该怎么办? 为什么不关注 this approach 并将@ mat&s和@ sharky的代码结合起来?
:- use_module(library(clpfd)). posAt__NEW(L0, Pos, L1) :- posAt__NEW_(L0, 1, Pos, L1). posAt__NEW_([X|_], Pos, Pos, [X|_]). posAt__NEW_([_|X0s], CurrPos, Pos, [_|X1s]) :- CurrPos #< Pos, NextPos #= CurrPos + 1, posAt__NEW_(X0s, NextPos, Pos, X1s).
让我们使用posAt__NEW/3
重新运行上面的示例查询:
?- numlist(1,1000,Zs), time(posAt__NEW(Zs,1000,Zs)). % 4,997 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 18141619 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 9 inferences, 0.000 CPU in 0.000 seconds (71% CPU, 122877 Lips) false. ?- time((posAt__NEW(_,_,[a,b,c]), false)). % 440 inferences, 0.001 CPU in 0.001 seconds (98% CPU, 803836 Lips) false. ?- numlist(1,1000,Zs), time((posAt__NEW(Zs,_,_), false)). % 154,955 inferences, 0.014 CPU in 0.014 seconds (100% CPU, 11067900 Lips) false. ?- posAt__NEW([a], 1, Xs). Xs = [a|_G229] ; false. ?- numlist(1,1000,Zs), time(posAt__NEW(Zs,1,Zs)). % 1 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 121818 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 7 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 266748 Lips) false.
好的!最后,我们确保上面3 rd 查询中使用的目标具有线性复杂性:
?- numlist(1,100,Zs), time((posAt__NEW(Zs,_,_), false)). % 15,455 inferences, 0.004 CPU in 0.004 seconds (100% CPU, 3545396 Lips) false. ?- numlist(1,1000,Zs), time((posAt__NEW(Zs,_,_), false)). % 154,955 inferences, 0.016 CPU in 0.017 seconds (98% CPU, 9456629 Lips) false. ?- numlist(1,10000,Zs), time((posAt__NEW(Zs,_,_), false)). % 1,549,955 inferences, 0.098 CPU in 0.099 seconds (99% CPU, 15790369 Lips) false. ?- numlist(1,100000,Zs), time((posAt__NEW(Zs,_,_), false)). % 15,499,955 inferences, 1.003 CPU in 1.007 seconds (100% CPU, 15446075 Lips) false.