不确定您使用的是什么术语,但 Lloyd 使用的通用术语,逻辑编程基础,是SLD-tree,它可能包含成功、无限和失败分支。我假设你使用标准的 Prolog 计算规则(= 选择最左边的原子)。
您示例中的 SLD 树是无限的,并且包含与三个答案替换相对应的有限多个成功分支:X = b ; X = c ; X = d
. 需要注意的是,虽然这棵树有一个无限的失败分支,但它仍然包含所有解决方案!
计算规则的选择可能会影响 SLD 树的大小和结构。但好的是:无论你的计算规则是什么,我们都会得到我们的成功分支!保证!嗯,保证为那些以聪明的方式绘制无限 SLD-tree,并且知道如何使用......明智的人。但对于拥有无限资源和睿智的人来说,没问题。
另一个观察结果是这个 SLD 树的形状完全独立于子句的顺序。因此,无论非递归规则是最后(如您的情况)还是首先编写,对树的形状都没有太大影响:它将具有相同的大小(无论是无限的还是都是有限的),将包含相同数量的节点,但某些分支会以不同的顺序出现。
Prolog 尝试以增量方式生成该 SLD-tree,但它使用最原始的方式来执行此操作。它开始探索一个分支,并在扩展其他分支之前完全探索它。这意味着,Prolog 可以使用 SLD-tree(或者更准确地说:仅 SLD-tree 的一部分)的一种非常节省空间的表示,实际上它本质上是一个堆栈,但要付出的代价是一旦遇到无限分支,您就会被卡住 - 除非您确实拥有无限资源。无论是否找到成功分支(=答案)或它们是否会被某个无限分支掩埋,子句的顺序都会产生影响。
在实践中,很难想象所有这些无限的树,我们不太习惯它们。
但是还有其他方法可以让您更好地理解 Prolog 如何执行其证明。
这里的中心概念是查询/目标的通用终止,在 SLD 树中表示:所有分支的有限性。
观察目标的普遍终止非常容易:只需执行即可Goal,
false
。
那么这false
对我们的 SLD 树做了什么?本质上,我们现在只有一个无限或有限的故障分支。所有的答案都没有了。
现在,事情变得更好了:我们甚至可以将false
其引入您的程序。生成的程序称为失败切片。尽管该程序不再与原始程序相同,但以下性质成立:如果故障片没有终止(= 具有无限分支),则原始程序也不会终止(= 具有无限分支)。
故障片通常要短得多,因此理解起来要快得多。参加你的计划:
?- 路径(a,X),假。
边缘(a,b):-假。
边缘(b,c):-假。
边缘(a,d):-假。
路径(N,M):- 路径(N,新),假,边缘(新,M)。
路径(N,M):-假,边缘(N,M)。
有经验的 Prolog 程序员只看到程序的这个小片段,这更容易理解。他们没有花时间想象无限的分支,也没有想象实际的 Prolog 执行——嗯,也许有一点,但只针对片段,而不是整个程序。
从某种意义上说,false
已经向我们展示了计算规则: 右侧的所有内容都false
被划过。
您也可以学习“看到”这个,只需点击此链接即可。