2013-05-01 129 views
3

在本书中,我们被要求使用以下布局来定义谓词left_of,right_of,above和below。从Prolog艺术中练习

% 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编码为事实递归过程?

+0

真的吗?它似乎对我来说工作正常。可能想要退出Prolog会话并再次加载代码以确保。 – 2013-05-01 20:03:18

+0

丹尼尔,我的问题是可以用于事实和递归规则(不必使用left_of2)。我可能会解释这本书的练习是错误的。 – stirfoo 2013-05-01 20:11:54

+1

在这种情况下,答案是否定的。 Prolog有许多场合,当你必须有单独的谓词时,你不需要在其他语言中使用它们(初始化或最终化步骤也会出现在循环中)。这只是其中之一。你总是可以用不同的名字来隐藏“内部”谓词(想到''''''''''''''''并且''left_of2''为您的用户'left_of'。 – 2013-05-01 20:18:43

回答

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的谓词“更简单”,曾经让我困扰。现在我很高兴他们更简单,因为还有足够的其他东西,我很高兴我也不需要找出变量参数谓词或关键字参数(尽管你可以很容易地用列表来模拟,如果你需要)。

+0

谢谢丹尼尔的解释。 Prolog是迄今为止我尝试的最具挑战性的语言。(scheme,cl,clojure,python,C++和C) – stirfoo 2013-05-01 20:47:45

+0

它起初很令人难以置信,它变得更好,更有趣,但保持怪异。 :)我很高兴你给它一个镜头,并且你正在阅读一本经典书籍,这将有助于你的FP背景。我很高兴能够帮助,并随时给我发电子邮件。 – 2013-05-01 21:04:07