【问题标题】:Finding query for which a prolog program gives incorrect result查找 prolog 程序给出错误结果的查询
【发布时间】:2014-06-30 18:22:08
【问题描述】:

这个 Prolog 程序将第三个参数定义为前两个数字参数的最大值:

max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).

我认为这个程序运行良好。但我被告知它会给出不正确的结果。你能说出时间和原因吗?

【问题讨论】:

  • 如果X,Y 不绑定整数或实数怎么办?
  • 我们假设参数是数值

标签: prolog max prolog-cut


【解决方案1】:

这是一个教科书的例子。

?- 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).

【讨论】:

  • max/3 的这个定义只有在所有参数都被实例化的情况下才有效。然而,人们通常不仅要测试,还要找到解决方案,例如,两个给定数字的最大值是多少?或者例如,哪些数字具有给定的最大值?此类问题的现代解决方案是约束,例如,具有有限域约束:M #= max(X, Y)。它可以全方位地用于寻找和测试解决方案。
  • @mat 绝对。但这毕竟是一个考试/辅导问题的明确案例,显然是在追随教科书的答案。我希望人们在大学学习课程时至少阅读“Prolog 的艺术”,Stackoverflow 的讨论终于可以从几十年前已经回答的问题转移到更绿色的牧场上。不幸的是,这并没有发生,Prolog 标签被非生产性地咀嚼过久的干涸的东西所淹没。
  • @mat 不是全部,但实际上至少是前两个。如果您使用 SWI-Prolog 表示法,它将是 max(+A, +B, -Max)
【解决方案2】:

只要第三个参数没有实例化,它就可以正常工作。这里的危险是如果有办法回溯到第二个规则,或者第三个参数被实例化为与第二个相同的值。这看起来不是特别安全,因为max(X, Y, Y). 等于max(_, Y, Y),它只是将结果设置为第二个值,而不加任何考虑。第一条规则末尾的剪切有效地确保了如果 X >= Y 则不会开始回溯,因此只有在 X

虽然它主要是有效的,但这并不是一个好习惯。刚接触 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会回溯,直到统一成功,然后再回溯,但是匹配的排他性会阻止另一个body被执行。如果我们发现回溯花费了太多时间,我们可以添加削减以提高性能,但实际上这不太可能成为问题。

【讨论】:

  • 您在第一段中的推理是错误的。当第二个和第三个参数实例化为相同的数字时,根本不考虑第一个子句。
  • 但是,当您希望它更大时,为什么要将第三个参数实例化为两者中较小的数字呢?
  • @Boris 好电话。会修复的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多