3

在本书中,我们被要求使用以下布局定义谓词 left_of、right_of、above 和 below。

% bike               camera
% pencil  hourglass  butterfly  fish

left_of(pencil, hourglass).
left_of(hourglass, butterfly).
left_of(butterfly, fish).

above(bike, pencil).
above(camera, butterfly).

right_of(Obj1, Obj2) :-
    left_of(Obj2, Obj1).

below(Obj1, Obj2) :-
    above(Obj2, Obj1).

这似乎找到了正确的解决方案。

在本书的后面,我们被要求为 left_of 添加一个递归规则。我能找到的唯一解决方案是使用不同的仿函数名称:left_of2。所以我基本上重新实现了祖先关系。

left_of2(Obj1, Obj2) :-
    left_of(Obj1, Obj2).
left_of2(Obj1, Obj2) :-
    left_of(Obj1, X),
    left_of2(X, Obj2).

在我尝试重用 left_of 时,我可以获得所有正确的解决方案,但在最后重做时,会发生堆栈溢出。我猜那是因为我没有定义正确的基本情况。可以使用 left_of 对事实递归过程进行编码吗?

4

1 回答 1

5

正如评论中提到的,不幸的是,在 Prolog 中,您必须单独命名谓词才能执行此操作。如果你不这样做,你最终会得到如下所示的东西:

left_of(X,Z) :- left_of(X,Y), left_of(Y,Z).

这给了你两次无限递归。共享相同名称的事实和谓词原则上没有错——事实上,基本案例规则看起来像事实是很常见的。只是处理像这样的传递闭包情况会导致堆栈溢出,除非两个步骤中的一个是有限的,并且在 Prolog 中除了单独命名它们之外没有其他方法可以确保这一点。

这远不是 Prolog 中唯一需要将工作分解为单独谓词的情况。其他常见的情况包括带有初始化器或终结器的计算循环。

按照惯例,人们最终会以与事实不同的方式命名谓词。例如,directly_left_of对于事实和left_of谓词。使用模块系统或 Logtalk,您可以轻松隐藏“直接”版本并鼓励您的用户使用传递版本。您还可以通过为隐藏的名称使用一个不舒服的名称(例如left_of_.

在其他语言中,函数是一种更不透明、更大的抽象,并且有一些工具可以将大量工作隐藏在函数后面。相比之下,Prolog 的谓词“更简单”,这曾经让我感到困扰。现在我很高兴它们更简单,因为还有足够多的其他东西我很高兴我也不必弄清楚可变数量谓词或关键字参数(尽管你可以很容易地用列表模拟两者,如果你需要)。

于 2013-05-01T20:28:52.100 回答