【问题标题】:Why does this simple Prolog example cause a stack overflow?为什么这个简单的 Prolog 示例会导致堆栈溢出?
【发布时间】:2016-03-30 07:46:34
【问题描述】:

我正在学习 Prolog。我写了几个简单的事实和规则:

heavier(X,Y) :- lighter(Y,X).
heavier(horse,mouse).
lighter(X,Y) :- heavier(Y,X).

然后我问了这个问题:

lighter(mouse,horse).

我收到以下错误:

Fatal Error: local stack overflow (size: 16384 Kb, reached: 16383 Kb, environment variable used: LOCALSZ)

这个程序有什么问题?

【问题讨论】:

    标签: prolog stack-overflow


    【解决方案1】:

    我在这里复制了您的条款,为方便起见编号:

    heavier(X,Y) :- lighter(Y,X).  %1
    heavier(horse,mouse).          %2
    lighter(X,Y) :- heavier(Y,X).  %3
    

    所以让我们弄清楚解释器要做什么。

    1. 你问过它lighter(mouse,horse). 匹配 3,与 X = mouse 和 Y = horse。
    2. 它现在需要知道heavier(horse, mouse) 是否为真。这匹配 1,与 X = horse 和 Y = mouse。
    3. 现在需要知道lighter(mouse,horse) 是否为真。匹配 3,与 X = mouse 和 Y = horse。 嘿,等一下!

    由于解释器从顶部开始,当评估heavier/2时,它总是从第一个子句开始,这使得它想要评估lighter/2,这使得它想要评估heavier/2,它以第一个子句...您可能会看到它的标题。

    但这不仅仅是无限循环。你看,当解释器决定使用第一个子句时,它知道还有另一个匹配的子句。每次它决定评估第一个子句时,它也会记住另一个选项。未使用的选项堆栈不断增长,不断增长......直到堆栈溢出。


    所以直接的问题是子句 1 和 2 的顺序。如果你切换它们,它首先会尝试评估 fact heavier(horse, mouse). 成功,所以你的查询 lighter(mouse,horse). 返回 @ 987654336@。太好了!

    但这只是其中的一半。让我们重新排序这些子句,并询问lighter(bird, horse).

    需要一些时间,不是吗?那是因为无限循环仍在发生。现在这个事实永远不会匹配,因为它涉及的是老鼠而不是鸟,而解释器只是不断地循环遍历你的循环定义。它没有任何要记住的选项,因此堆栈永远不会溢出(或者至少没有那么快),但我们仍然没有得到答案。

    对此的解决方案是将您的事实与您的定义分开。

    heavier(X, Y) :- is_heavier(X, Y).
    heavier(X, Y) :- is_lighter(Y, X).
    
    lighter(X, Y) :- heavier(Y, X).
    
    is_heavier(horse, mouse).
    is_lighter(bird, horse).
    

    这应该可以解决问题。

    【讨论】:

    • 分层? s(X)!
    【解决方案2】:

    您可以使用trace/0 来检查程序是如何工作的,并且您会知道,简单地说,规则heavier(X,Y) :- lighter(Y,X). 在heavier(horse,mouse). 之前,所以永远不会达到第二条规则,这就是为什么您的程序只是无限循环

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-20
      • 2013-12-30
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 2011-12-21
      • 1970-01-01
      相关资源
      最近更新 更多