以下是Prolog代码的一部分,以递归的方式定义数字:
numeral(0).
numeral(succ(X)) :- numeral(X).
给定查询numeral(X).
时,Prolog将返回:
X = 0 ;
X = succ(0) ;
X = succ(succ(0)) ;
X = succ(succ(succ(0))) ;
X = succ(succ(succ(succ(0)))) ;
X = succ(succ(succ(succ(succ(0))))) ;
X = succ(succ(succ(succ(succ(succ(0)))))) ;
X = succ(succ(succ(succ(succ(succ(succ(0))))))) ;
X = succ(succ(succ(succ(succ(succ(succ(succ(0))))))))
yes
根据我所学的知识,进行查询时,prolog首先会将X
变成(_G42)
之类的变量,然后它将搜索事实和规则以查找匹配项。
在这种情况下,它将找到0
(事实)作为正确的匹配项。然后它将尝试匹配该规则。也就是说,_G42
不是0
,而_G42
是另一个数字的成功。因此,将生成另一个变量(如_G44
),_G44
将与0
匹配,并且还将更进一步_G42
。由于_G44
与0
相匹配,因此它将退回到_G42
,得到_G42 = succ(_G44) = succ(0)
。
我不确定我是否理解正确。我制作了一个图表来显示我对这个问题的理解。
如果分析正确,那么仍然很难设计这样的递归函数。因为我是Prolog的新手,所以我想知道这种定义是否总是在应用程序中使用(例如,构建专家系统,验证协议),还是仅仅是让初学者更好地了解基本搜索过程?如果经常使用,那么设计这种递归定义的关键是什么?
答案 0 :(得分:4)
我的个人观点:尤其是,作为初学者,您有零机会“了解Prolog中的递归搜索”。无数的初学者正在尝试以这种方式理解Prolog,并且他们非常一致失败。
可悲的是,这使最努力的工人受到的打击最大:您始终认为您可以以某种方式理解它,但是最后,您不能,因为有太多的方法可以调用甚至最简单的方法谓词,带有未实例化和(部分)实例化的参数,甚至带有别名变量。
您的图表很好地说明了即使对于最简单的可想像的递归定义,这样的过程性阅读也会非常迅速地变得笨拙。
理解谓词的很多更易处理的方法是以声明方式阅读:
0
是一个数字X
是数字(无论X
是什么!),则succ(X)
中的然后 X
为也是一个数字。请注意,:-
甚至表示←,即从右到左的含义。
我的建议是着眼于对应该持有什么的明确的声明性描述。为了克服Prolog的最初障碍,您必须放开这样的想法,即可以跟踪CPU当前正在遵循的极其详细的步骤。 Prolog太高级,无法以这种低级方式进行跟踪。就像通过仅跟踪说话者的神经活动来尝试在法语和英语之间进行解释。
写下明确的定义,然后将搜索保留到Prolog 。有许多其他的和可行的方法可以理解和分解声明式定义,而不会陷入低级的细节中。例如,请参见program-slicing和failure-slicing。只要您停留在Prolog的所谓纯单调子集中,它们就会起作用。专注于这一领域,您将能够快速进步。