我们从第35页的例3.2中的家族关系开始。小知识库KB被编码 – 没有谓词女性的事实– 作为图5.1中名为rel.pl的PROLOG程序。
可以使用该命令在PROLOG解释器中加载和编译该程序
? – [rel]。
1.
简单例子69
初始查询返回对话框
? – 孩子(夏娃,奥斯卡,安妮)。
是
正确答案是的。这个答案是如何产生的?对于查询“ ? –
child(eve,oscar,anne)。“在其条款标题中有六个事实和一个规则具有相同的谓词。现在,在查询与输入数据中的每个互补文字之间按照发生的顺序进行统一。如果其中一个替代方案失败,则会导致回溯到最后一个分支点,并测试下一个替代方案。因为统一在每个事实中都失败了,所以查询与第8行中的递归规则统一。现在系统尝试解决子目标子(eve,anne,oscar),这与第三种选择成功。查询
? – 后代(X,Y)。X =奥斯卡
Y =凯伦是的
回答第一个找到的解决方案,原样
? – 后裔(克莱德,Y)。Y =玛丽
是
查询
? – 后裔(克莱德,卡伦)。
然而,没有回答。其原因是第8行中的子句,它指定了子谓词的对称性。该子句以递归方式调用自身而不具有终止的可能性。这个问题可以通过以下新程序解决(这里省略了事实)。
1.
后代(X,Y): – 子(X,Y,Z)。
2.
后代(X,Y): – 子(X,Z,Y)。
3.
后代(X,Y): – 子(X,U,V),后代(U,Y)。
但现在查询
? – 孩子(夏娃,奥斯卡,安妮)。
不再正确回答,因为不再给出最后两个变量中的子对称性。该程序中找到了解决这两个问题的方法
4.
child_fact(奥斯卡,克伦,弗朗兹)。
5.
child_fact(玛丽,克伦,弗朗兹)。
6.
child_fact(EVA,安,奥斯卡)。
7.
child_fact(亨利,安,奥斯卡)。
8.
child_fact(伊索尔德,安,奥斯卡)。
9.
child_fact(克莱德,玛丽,oscarb)。7
10.
child(X,Z,Y): – child_fact(X,Y,Z)。
11.
child(X,Z,Y): – child_fact(X,Z,Y)。10
12.
后代(X,Y): – 子(X,Y,Z)。
13.
后代(X,Y): – 子(X,U,V),后代(U,Y)。
通过为事实引入新的谓词child_fact,谓词子节点不再是递归的。但是,该程序不再像第68 页的图5.1中的逻辑正确的第一个变体那样优雅和简单,这导致了无限循环。与其他语言一样,PROLOG程序员必须注意处理并避免无限循环。PROLOG只是一种编程语言,而不是一个定理证明者。
我们必须在这里区分PROLOG程序的声明和程序语义。声明性语义由喇叭子句的逻辑解释给出。相反,程序语义是由PROLOG程序的执行定义的,我们希望现在更详细地观察它。使用查询子项(eve,oscar,anne)执行第 68 页的图 5.1 中的程序,如第 71 页的图
5.2 所示,作为搜索
树。1执行从左上角开始查询。每条边代表一个位置
sible SLD解决步骤与互补的统一文字。当搜索树通过递归规则变得无限深时,PROLOG执行终止,因为事实发生在输入数据中的规则之前。
相反,对于查询后代(clyde,karen),PROLOG执行不会终止。我们可以在第71 页的图5.3中显示的和/或树中清楚地看到这一点。在这种表示中,由一个条款的头部引导到子目标的分支代表。因为必须解决子句的所有子目标,所以这些是和分支。所有其他分支是或分支,其中至少一个必须与其父节点一致。这两个概述的事实代表了
1 常量缩写为节省空间。
2.
执行控制和程序要素71
图5.2 儿童的PROLOG搜索树(夏娃,奥斯卡,安妮)
查询的解决方案。然而,PROLOG解释器并没有在这里终止,因为它通过使用带有回溯的深度优先搜索(参见第6.2.2节)来工作,因此首先选择到最左边的无限深的路径。