【问题标题】:Why do circular references and recursion make my program fail?为什么循环引用和递归会使我的程序失败?
【发布时间】:2011-12-27 20:40:31
【问题描述】:

我编写了这个简单的 Prolog 程序。

man(socrates).
mortal(X) :- man(X).
immortal(X) :- immortal(X).

我问了一些常见的问题,比如苏格拉底是人还是苏格拉底是凡人。

?- man(socrates).
true.                    //we know for a fact that Socrates is a man
?- mortal(socrates).
true.                    //and it can logically be inferred that Socrates is mortal
?- immortal(socrates).
                         //but we can't seem to figure out if he's immortal

由于immortal 的递归定义,它崩溃了。循环引用也会使其崩溃或出现 Out of stack space. 错误

在我看来,至少在这种情况下,Prolog 先生从程序中的规则得出结论,不能推断苏格拉底是不朽的,这将是相当微不足道的。如何?我想它可以检查堆栈,看看它是否正在遍历一个已经被遍历过的规则。

这是否尚未实施的原因?这样做会不会有一些我忽略的问题,或者是否存在已经执行此类分析的 Prolog 实现?

【问题讨论】:

  • 你是说神仙的定义就是神仙?
  • 您正在寻找“表格”,尽管只有少数 prolog 实现支持它(例如 XSB 或 YAP)。

标签: recursion prolog infinite-loop circular-reference


【解决方案1】:

在我看来,至少在这种情况下,Prolog 先生从程序中的规则得出结论,不能推断苏格拉底是不朽的,这将是相当微不足道的。

Prolog 使用不完全推理算法来提高效率。它是一种编程语言,其中程序除了程序性之外还具有逻辑意义,而不是成熟的定理证明器。您必须注意编写子句的顺序,防止循环定义等。

至于你的谓词immortal的逻辑含义是

immortal(X) -> immortal(X)

这是一个重言式,可以在不改变其逻辑含义的情况下从您的程序/理论中删除。这意味着如果这有助于改善程序意义(摆脱无限循环),您应该删除它。

【讨论】:

    【解决方案2】:

    通过XSB 使用表格:

    :- table foo/1.
    
    foo(X) :- foo(X).
    
    bar(X) :- bar(X).
    

    然后:

    | ?- [tabled].
    [tabled loaded]
    
    yes
    | ?- foo(1).
    
    no
    | ?- bar(1).    % does not finish
    

    【讨论】:

      【解决方案3】:

      您的定义 - 以及您如何解释它们:

      man(socrates).
      

      苏格拉底是个男人。

      mortal(X) :- man(X).
      

      每个人都是凡人。

      immortal(X) :- immortal(X).
      

      每一个不朽的都是不朽的。


      您的定义 - 以及 Prolog 如何解释它们:

      man(socrates).
      

      如果你问苏格拉底的男子气概,我知道这是真的。

      mortal(X) :- man(X).
      

      如果你问我某人的死亡率,我会检查他的男子气概(如果这是真的,死亡率也是如此)。

      immortal(X) :- immortal(X).
      

      如果你问我关于某人的不朽,我会检查他的不朽。 (您是否仍然想知道这如何导致无限循环?)


      如果你想声明某人是不朽的,如果他不能被证明是凡人,那么你可以使用:

      immortal(X) :- not( mortal(X) ).
      

      【讨论】:

      • 我理解为什么它可能是一个无限循环,但我认为逻辑语言能够得出结论immortal 总是错误的。例如,假设 Stack Overflow 上有一个“递归”徽章,而获得递归徽章的唯一方法就是获得递归徽章,这是不可能获得的。
      • 如果您希望 immortal 始终为 false,您可以明确声明 immortal(X) :- False.immortal(X) :- !. (我可能错了,很多年我没有使用 Prolog)。对于这个简单的情况,它们是等价的,但您可以有其他更复杂的情况,其中递归非常有用(可以用! 停止)
      • immortal(X) :- !. 错了,会说每个X都是不朽的。
      • @PeterOlson,更正:如果你想让 immortal 永远是假的,你可以明确声明 immortal(X) :- False.,这意味着每个人都不是不朽的。 (而不是 immortal(X) :- !.,正如 Joe Lehmann 指出的那样,这意味着相反,每个人都是不朽的
      • @Peter Olson:这是非常不同的。调用基于 ISO-Prolog 的未定义谓词不会失败​​,但会出现错误。
      【解决方案4】:

      这个小程序怎么样:

       loopy(Y) :- read(X), Z is X+Y, print(Z), nl, loopy(Y).
      

      Prolog 先生会推断,loopy(Y) 已经被调用并且会失败。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-12
        • 1970-01-01
        • 2013-10-17
        • 1970-01-01
        • 1970-01-01
        • 2022-08-03
        • 2018-05-15
        • 2023-03-22
        相关资源
        最近更新 更多