我在Prolog中实现了以下电源程序:
puissance(_,0,1).
puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.
代码执行应该执行的操作,但在正确答案之后,它会打印“false”。我不明白为什么。我正在使用swi-prolog。
答案 0 :(得分:3)
可以这样做:
puissance(X,N,P) :-
( N > 0 ->
A is N-1,
puissance(X,A,Z),
P is Z*X
; P = 1 ).
然后它会打印一个答案。
(你的代码在每次递归调用时留下一个“选择点”,因为你有两个析取而没有剪切。使用if-then-else或某个地方的剪切会删除那些。然后它取决于解释器会发生什么。 Sicstus仍然询问你是否想要((试图找到))更多答案。)
答案 1 :(得分:2)
目前,puissance/3
有3个不同的版本,我想在其中一些版本之间显示出明显的语义差异。
作为测试用例,我考虑了查询:
?- puissance(X, Y, Z), false.
此查询的含义是什么?声明地说,它显然等同于 false 。不过,这个查询非常有趣,因为它会终止 iff puissance/3
普遍终止。
现在,让我们尝试对程序的不同变体进行查询:
原始定义(来自问题):
?- puissance(X, Y, Z), false.
ERROR: puissance/3: Arguments are not sufficiently instantiated
接受的答案:
?- puissance(X, Y, Z), false.
false.
其他答案:
?- puissance(X, Y, Z), false.
ERROR: puissance/3: Arguments are not sufficiently instantiated
显然,接受的答案中显示的解决方案产生了不同的结果,值得进一步考虑。
这是程序:
puissance(_,0,1) :- !. puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.
让我们先问一些简单的事情:哪些解决方案?这称为最一般的查询,因为它的参数都是新变量:
?- puissance(X, Y, Z). Y = 0, Z = 1.
该计划回答:只有单一解决方案:Y=0, Z=1
。
不正确(要查看此内容,请尝试成功的查询?- puissance(0, 1, _)
,这与相同的程序相反,声称{ {1}}只能是Y
),与问题中显示的程序有显着差异。为了比较,原始程序产生:
?- puissance(X, Y, Z). Y = 0, Z = 1 ; ERROR: puissance/3: Arguments are not sufficiently instantiated
没关系:在回溯时,程序会抛出实例化错误,表示此时无法进一步推理。但至关重要的是,不只会失败!
所以,让我们坚持原始程序,并考虑查询:
?- puissance(1, 1, Z). Z = 1 ; false.
我们希望摆脱0
,因为该程序不确定而发生。
解决此问题的一种方法是使用false
中的zcompare/3
。这使得 reify 比较,并使结果可用于索引,同时保留谓词的一般性。
以下是一种可能的解决方案:
puissance(X, N, P) :- zcompare(C, 0, N), puissance_(C, X, N, P). puissance_(=, _, 0, 1). puissance_(<, X, N, P) :- A #= N-1, puissance(X, A, Z), P #= Z*X.
有了这个版本,我们得到:
?- puissance(1, 1, Z). Z = 1.
现在已按预期确定性。
现在,让我们从这个版本考虑上面的测试用例:
?- puissance(X, Y, Z), false. nontermination
啊哈!因此,此查询既不会抛出实例化错误也不会终止,因此与迄今为止发布的所有版本不同。
让我们考虑使用此程序的最常规的查询:
?- puissance(X, Y, Z). Y = 0, Z = 1 ; X = Z, Y = 1, Z in inf..sup ; Y = 2, X^2#=Z, Z in 0..sup ; Y = 3, _G3136*X#=Z, X^2#=_G3136, _G3136 in 0..sup ; etc.
啊哈!因此,我们得到满足这种关系的所有整数的符号表示。
这很酷,因此我建议你在推理Prolog中的整数时使用CLP(FD)约束。这将使您的程序更加通用,也可以让您更轻松地提高效率。
答案 2 :(得分:0)
您可以在解决方案中添加 cut 运算符(即!
),这意味着prolog不应该尝试回溯并在第一次成功统一后找到更多解决方案。 (即你正在修剪解决方案树)。
puissance(_,0,1) :- !.
puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.
Layman的解释:
prolog试图查看是否还有其他解决方案的原因是:
在你的递归中最后一次调用puissance
时,第一个 puissance
子句成功,因为P = 1,你一路旅行回到最高呼叫与P进行统一,并使用该选择产生的最终值。
但是,对于最后一次调用puissance
,Prolog没有机会检查第二个 puissance
子句是否 是否可以满足并可能导致不同的解决方案,因此,除非您告诉它不检查进一步的解决方案(通过在第一个条款成功后使用剪切),它有义务回到那一点,并检查第二条也是。
一旦这样做,就会发现第二个子句无法满足,因为N = 0,因此该特定尝试失败。
因此,“假”有效地意味着prolog也检查了其他选择点,并且无法以任何其他方式统一P以满足它们,即P没有更有效的统一。
事实上,您首先可以选择外观寻找其他解决方案,这恰恰意味着还有其他路线可能还有尚未探索过的可能令人满意的条款。