左派从Prolog的艺术演习

sti*_*foo 3 prolog

在本书中,我们被要求使用以下布局定义谓词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).
Run Code Online (Sandbox Code Playgroud)

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

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

left_of2(Obj1, Obj2) :-
    left_of(Obj1, Obj2).
left_of2(Obj1, Obj2) :-
    left_of(Obj1, X),
    left_of2(X, Obj2).
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*ons 5

正如评论中所提到的,一个不幸的事实是,在Prolog中你必须有单独命名的谓词才能做到这一点.如果你不这样做,你会看到这样的东西:

left_of(X,Z) :- left_of(X,Y), left_of(Y,Z).
Run Code Online (Sandbox Code Playgroud)

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

这远非Prolog中唯一一个你被迫将工作分解为单独的谓词的情况.其他常见情况包括具有初始化器或终结器的计算循环.

传统上,人们最终会以不同的方式命名谓词.例如,directly_left_of对于事实和left_of谓词.使用模块系统或Logtalk,您可以轻松隐藏"直接"版本,并鼓励用户使用传递版本.你也可以通过对隐藏的名称使用一个令人不舒服的名字来使意图更加明确,而不是禁止它left_of_.

在其他语言中,函数是一种更不透明,更大的抽象,并且有一些设施可以隐藏相当多的工作.相比之下,Prolog的谓词"更简单",曾经困扰过我.现在我很高兴它们更简单,因为还有足够的其他内容,我很高兴我也不必弄清楚变量的谓词或关键字参数(尽管你可以很容易地用列表模拟它们,如果你需要).