【问题标题】:Prolog program returns falseProlog 程序返回 false
【发布时间】:2017-02-06 22:43:37
【问题描述】:

我在 Prolog 中实现了以下电源程序:

puissance(_,0,1).
puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.

代码做了应该做的事情,但是在正确答案之后它会打印“假”。我不明白为什么。我正在使用 swi-prolog。

【问题讨论】:

  • 这表示没有进一步的解决方案

标签: prolog prolog-toplevel


【解决方案1】:

可以这样做:

puissance(X,N,P) :-
  ( N > 0 ->
    A is N-1,
    puissance(X,A,Z),
    P is Z*X
  ; P = 1 ).

然后它只会打印一个答案。

(您的代码在每次递归调用时都会留下一个“选择点”,因为您有两个析取项且没有剪切。在某处使用 if-then-else 或剪切会删除这些。然后取决于解释器会发生什么。Sicstus 仍然询问您是否想要((试图找到))更多答案。)

【讨论】:

    【解决方案2】:

    语义差异

    目前,puissance/3 有 3 个不同版本,我想展示其中一些之间的显着语义差异。

    作为一个测试用例,我考虑查询:

    ?- puissance(X, Y, Z), false

    这个查询是什么意思?声明式地,它显然等同于 false。不过,这个查询非常有趣,因为它终止了 iff puissance/3 普遍终止

    现在,让我们尝试对程序的不同变体进行查询:

    1. 原始定义(来自问题):

      ?- puissance(X, Y, Z), false.
      ERROR: puissance/3: Arguments are not sufficiently instantiated
      
    2. 接受的答案:

      ?- puissance(X, Y, Z), false.
      false.
      
    3. 其他答案:

      ?- puissance(X, Y, Z), false.
      ERROR: puissance/3: Arguments are not sufficiently instantiated
      

    显然,接受答案中显示的解决方案会产生不同的结果,值得进一步考虑。

    程序又来了:

    puissance(_,0,1) :- !. puissance(X,N,P) :- N>0,A 是 N-1, puissance(X,A,Z), P 是 Z*X。

    让我们先问一个简单的问题:有哪些解决方案?这被称为最通用查询,因为它的参数都是新鲜的变量:

    ?- puissance(X,Y,Z)。 Y = 0, Z = 1。

    程序回答:只有单一解决方案Y=0, Z=1

    这是不正确(要查看此内容,请尝试查询 ?- puissance(0, 1, _) which succeeds,与声称 Ysame 程序相反只能是0),和问题中显示的程序有很大区别。为了比较,原始程序产生:

    ?- puissance(X,Y,Z)。 Y = 0, Z = 1 ; 错误:puissance/3:参数没有充分实例化

    没关系:在回溯时,程序会抛出实例化错误,表明此时无法进行进一步推理。但至关重要的是,它确实只是失败了!

    改进确定性

    所以,让我们坚持原来的程序,并考虑查询:

    ?- puissance(1, 1, Z)。 Z = 1 ; 错误。

    我们想摆脱false,这是因为程序不确定

    解决此问题的一种方法是使用来自library(clpfd)zcompare/3。这可以让您具体化比较,并使结果可用于索引,同时保留谓词的一般性。

    这是一种可能的解决方案:

    puissance(X, N, P) :- zcompare(C, 0, N), puissance_(C, X, N, P)。 puissance_(=, _, 0, 1)。 puissance_(

    有了这个版本,我们得到:

    ?- puissance(1, 1, Z)。 Z = 1。

    这现在是确定性,正如预期的那样。

    现在,让我们考虑上面这个版本的测试用例:

    ?- puissance(X, Y, Z), false不终止

    啊哈!因此,此查询既不会引发实例化错误,也不会终止,因此与迄今为止发布的所有版本都不同。

    让我们考虑一下这个程序的最通用查询:

    ?- puissance(X,Y,Z)。 Y = 0, Z = 1 ; X = Z, Y = 1, Z inf..sup ; Y = 2, X^2#=Z, Z in 0..sup ; Y = 3, _G3136*X#=Z, X^2#=_G3136, _G3136 在 0..sup ; 等等

    啊哈!所以我们得到了满足这个关系的所有整数的符号表示。

    这很酷,因此我建议您在 Prolog 中对整数进行推理时使用 CLP(FD) 约束。这将使您的程序更通用,也让您更轻松地提高其效率。

    【讨论】:

      【解决方案3】:

      您可以在解决方案中添加 cut 运算符(即!),这意味着在第一次成功统一达到该点之后,prolog 不应尝试回溯并找到更多解决方案。 (即您正在修剪解决方案树)。

      puissance(_,0,1) :- !.
      puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.
      

      外行的解释:

      prolog 尝试查看是否有更多解决方案的原因是:

      在递归中最后一次调用 puissance 时,first puissance 子句自 P=1 以来成功,并且您一路返回顶部调用以执行与 P 的统一以及该选择产生的最终价值。

      然而,对于puissance 的最后一次调用,Prolog 没有机会检查 second puissance 子句是否可以满足并且可能导致不同的解决方案,因此除非您告诉它不要检查进一步的解决方案(通过在第一个子句成功后使用剪切),它有义务回到那个点,并检查第二个子句。

      一旦这样做,它就会发现第二个子句不能满足,因为 N = 0,因此该特定尝试失败。

      所以“假”实际上意味着 prolog 也检查了其他选择点,并且无法以任何其他方式统一 P 以满足它们,即没有更多有效的统一 P。

      而事实上,您首先可以选择寻找其他解决方案,这恰恰意味着还有其他路线,其中可能存在尚未探索的可满足子句。

      【讨论】:

      • 请注意,此处另一个答案中的“条件”语法阻止进一步回溯的原因正是因为-> 条件运算符is defined using cut under the hood。 (即,它更多的是需要注意的副作用,而不是人们可能期望“if then else”构造在 prolog 的上下文中应该做的事情)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-05
      • 2017-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多