正如我们在家庭关系示例中看到的那样,控制PROLOG的执行非常重要。避免不必要的回溯尤其会导致效率的大幅提升。为此目的的一种方法是切割。通过在子句中插入一个例外标记,我们可以防止在这一点上进行回溯。在下面的程序中,谓词max(X,Y,Max)计算两个数字X和Y的最大值。
1 max(X,Y,X): – X> = Y.
2 max(X,Y,Y): – X <Y。
如果第一个案例(第一个条款)适用,则不会达到第二个案例。另一方面,如果第一种情况不适用,则第二种情况的条件为真,这意味着不需要检查。例如,在查询中
? – max(3,2,Z),Z> 10。
因为Z = 3而使用回溯,并且第二个子句被测试为max,这注定要失败。因此,不必回溯该点。我们可以通过切割来优化它:
1 max(X,Y,X): – X> = Y ,!
2 max(X,Y,Y)。
因此,只有在确实需要时才调用第二个子句,即第一个子句失败。但是,这种优化使程序更难理解。
执行控制的另一种可能性是内置谓词失败,这是永远不会发生的。在家庭关系示例中,我们可以使用查询打印出所有孩子及其父母
? – child _fact(X,Y,Z),write(X),write(‘是孩子的‘),写(Y),写(‘和‘),写(Z),写(‘。‘ ),nl,失败。
相应的输出是
奥斯卡是卡伦和弗兰克的孩子。玛丽是凯伦和弗兰克的孩子。前夕是安妮和奥斯卡的孩子。
…
没有。
其中谓词nl导致输出中的换行符。在不使用失败谓词的情况下,最终的输出是什么?
使用相同的知识库,查询“ ? – child_fact(ulla,X,Y)。“会得到答案否,因为没有关于ulla的事实。这个答案在逻辑上是不正确的。具体而言,不可能证明没有名称ulla的对象。在这里,证明者E会正确回答“ 没有找到证据。“因此,如果PROLOG回答否,这只意味着查询Q不能
1.
列表73
¬
被证明。但是,为此,Q不一定要被证明。此行为称为否定为失败。
在大多数情况下,限制自己使用Horn条款不会造成大问题。但是,对于使用SLD分辨率的程序执行很重要(第2.5节)。通过单独确定的正文字条款,SLD解决方案,以及PROLOG程序的执行,在该条款中有一个独特的切入点。这是可以重复执行逻辑程序的唯一方法,因此,定义明确的过程语义。
实际上,Horn条款无法描述问题陈述。一个例子是第45页的例3.7中的Russell悖论,其中包含非Horn子句(剃须(理发,X)∨剃须(X,X))。
使用PROLOG进行逻辑编程:清单
作为一种高级语言,PROLOG与语言LISP一样,具有方便的通用列表数据类型。具有元素A,2,2,B,3,4,5 的列表具有该形式
[A,2,2,B,3,4,5]
构造[Head |
Tail]将第一个元素(Head)与列表的其余(Tail)分开。有了知识库
列表([A,2,2,B,3,4,5])。
PROLOG显示对话框
? – 列表([H | T])。
H = A.
T = [2,2,B,3,4,5]
是
通过使用嵌套列表,我们可以创建任意树结构。例如,两棵树
§ 和a
BCBC
可以分别用列表[b,c]和[a,b,c]和两棵树来表示
•
§
• d
和
一个
BCD
e f ghe f
gh
通过列表[[e,f,g],[h],d]和[a,[b,e,f,g],[c,h],d]分别。在内部节点包含符号的树中,符号是列表的头部,子节点是尾部。
列表处理的一个很好的优雅示例是用于将列表Y附加到列表X的谓词附加(X,Y,Z)的定义。结果保存在Z中。相应的PROLOG程序读取
1追加([],L,L)。
2追加([X | L1],L2,[X | L3]): – 追加(L1,L2,L3)。
这是一个声明(递归)逻辑的一个事实,即描述L3从追加导致L2到L1。然而,与此同时,该程序在调用时也能完成工作。电话
? – 追加([a,b,c],[d,1,2],Z)。
返回替换Z = [a,b,c,d,1,2],就像调用一样
? – 追加(X,[1,2,3],[4,5,6,1,2,3])。
得到替换X = [4,5,6]。在这里我们观察到追加不是一个两位的函数,而是一个三位的关系。实际上,我们也可以输入“输出参数” Z并询问是否可以创建它。
反转列表元素的顺序也可以通过递归谓词进行优雅描述和同步编程
1 nrev([],[])。
2 nrev([H | T],R): – nrev(T,RT),附加(RT,[H],R)。
这会将列表的反转减少到一个较小的元素列表的反转。实际上,由于调用append,这个谓词的效率非常低。该程序称为天真反转,通常用作PROLOG基准(参见第81 页的练习5.6)。当使用临时存储(称为累加器)进行如下操作时情况会变得更好:
列出累加器
[A
B C D] []
[b,c,d] [a]
[c,d] [b,a]
[d] [c,b,a]
[]
[d,c,b,a]
2.
自我修改计划75
1.
accrev([],A,A)。
2.
accrev([H | T],A,R): – accrev(T,[H | A],R)。