为什么这个简单的Prolog示例会导致堆栈溢出?

时间:2016-03-30 07:46:35

标签: prolog stack-overflow

我正在学习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)

这个程序有什么问题?

2 个答案:

答案 0 :(得分:2)

我已在此处复制您的条款,为方便起见编号:

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

因此,让我们弄清楚口译员将要做什么。

  1. 你问lighter(mouse,horse).匹配3,X = mouseY = horse
  2. 现在需要知道heavier(horse, mouse)是否属实。匹配 1 X = horseY = mouse
  3. 现在需要知道lighter(mouse,horse)是否属实。匹配3,X = mouseY = horse嘿,等一下!
  4. 由于解释器从顶部开始,在评估heavier/2时,它总是从第一个子句开始,这使得它想要评估lighter/2,这使得它想要评估heavier/2 },从第一个子句开始......你可能会看到它的前进方向。

    但它不仅仅是无限循环。你看,当解释器决定使用第一个子句时,知道还有另一个匹配的子句。每次它决定评估第一个条款时,它都记得其他选项。堆栈的未使用选项会增长,增长和增长......直到堆栈流过。

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

    但那只是它的一半。让我们对这些条款重新排序,并询问是否true

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

    解决这个问题的方法是将你的事实与你的定义分开。

    lighter(bird, horse).

    这应该可以解决问题。

答案 1 :(得分:1)

您可以使用trace/0检查程序的工作方式,而且您会知道,heavier(X,Y) :- lighter(Y,X).规则就在heavier(horse,mouse).之前,因此永远不会达到第二条规则,这就是为什么您的程序是只是无限循环