这个Prolog程序将第三个参数定义为前两个数字参数的最大值:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
我认为这个程序运行得很好。但我被告知它可能会给出不正确的结果。你能说出何时以及为何?
答案 0 :(得分:3)
这是一本教科书的例子。
?- max(5,1,1).
true.
家庭作业:为什么程序错了?我们如何使程序正确?
修改强>
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
我们的意图是:
如果X大于Y,则然后 Max为X. 否则,Max必须为Y.
相反,说的是:
当第一个和第三个参数(X和Max)可以统一,并且X大于Y时,成功。否则,如果第二个和第三个参数(Y和Max)可以统一,则成功。
出现了明显的问题,那么第一和第三个参数不能统一,但第二个和第三个参数可以。
相反:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
或
max(X, Y, Max) :- X >= Y, !, Max = X.
max(_, Max, Max).
答案 1 :(得分:0)
如果第三个参数未实例化,它确实可以正常工作。这里存在的危险是,如果有一种方法可以回溯到第二个规则,或者第三个参数被实例化为与第二个规则相同的值。它看起来不是特别安全,因为max(X, Y, Y).
等于max(_, Y, Y)
,它只是将结果设置为第二个值而没有任何想法。如果X> = Y,则第一规则结束时的切割有效地确保不会开始回溯,因此仅当X <= Y时才应该输入第二规则。 Y和Z不等于Y.
虽然它主要起作用,但这并不是一个好习惯。 Prolog的新手倾向于在程序上思考并利用这样的剪辑来确保特定的结果通过程序性的欺骗最终阻碍你并导致复杂的Prolog无法以不同和有趣的方式驱动。编写这个谓词的其他几种方法同样有效,但不依赖于切割来确保它们的行为,例如:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
或
max(X, Y, Z) :- X >= Y -> Z = X ; Z = Y.
这些都不容易受到第三个被实例化的问题的影响。有趣的是,这是红色切割和绿色切割之间差异的一个很好的例证。你的代码有一个红色切割,其行为取决于切割,但如果我只是改变我的第一个解决方案:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y) :- X < Y.
这是一个绿色削减,因为行为不依赖于削减,但Prolog的表现可能略有改善,因为它不会回溯到第二个条款尝试它。在这里我们明确地告诉Prolog,不要两个都进行下一次检查,因为我们知道它会失败。红色切割,没有其他检查会失败。
令人遗憾的是,说明这种情况两次感觉多余,但依靠单一规则感觉很笨拙。在实践中,我的经验是,像这样的场景最终并不是那么常见;通常你有原子或结构,你可以在条款的头部匹配,创造我们在我的第一个替代品中的行为,但不需要身体。例如:
perform(scan(target, X, Y)) :- ...
perform(scan(calibration, X)) :- ...
这具有相同的效果:Prolog将回溯直到它成功统一,然后它将再次回溯,但匹配的专有性将阻止另一个身体被执行。如果我们发现它花费太多时间回溯我们可以增加削减以改善性能,但在实践中它不太可能是一个问题。