Learn Prolog Now 翻译 - 第二章 - 合一和证明搜索 - 第二节, 证明搜索

证明搜索

上一节我们已经学习了合一,本节我们继续学习Prolog是如何通过搜索知识库去决定输入的查询是否能够满足。我们将会学习证明搜索,并通过简单的一个例子去涵盖这个基础的概念。

假设我们有如下的知识库:

f(a).
f(b).

g(a).
g(b).

h(b).

k(X) :- f(X), g(X), h(X).

如果我们查询:

?- k(Y).

这个查询的结果十分明显,即k(b),但是Prolog是如何将其求解出的了?让我们继续看。

Prolog读入整个知识库,然后尝试将查询k(Y)和知识库中的事实或者规则头部进行合一。搜索知识库是自上而下的,并在第一个可能合一的位置,找到对应的信息。这个例子只是有一种

可能:k(Y)和规则,k(X) :- f(X), g(X), h(X) 的头部合一。

当Prolog将查询中的变量和事实或者规则中的变量合一时,会生成一个新的变量(比如_G34)去代表共享值,所以原始的查询转换为:

k(_G34)

并且Prolog知识库中的规则转换为:

k(_G34) :- f(_G34), g(_G34), h(_G34).

现在是什么情况了?查询说:“我想要找到符合属性k的人”。规则说:“如果要找到符合属性k的人,那么这个人也必须符合属性f,g和h”。所以如果Prolog找到符合属性k,g和h的信

息,那么就能满足原始的查询。所以Prolog使用下面的目标去替代原始查询:

f(_G34), g(_G34), h(_G34).

如果使用图形演示,会使得这个过程更加形象,如下:

在盒子中的都是查询或者目标。具体地讲,我们的原始目标是证明k(Y),所以它出现在最顶层的盒子中。当我们将k(Y)和数据库中的规则头部合一时,_G34作为分享值的新中间变量,

被赋值给X,Y,所以我们有了新的目标:f(_G34), g(_G34), h(_G34),出现在第二个盒子中。

现在,无论是否存在一系列的子目标需要证明,Prolog都会通过自左向右的顺序,尝试一个一个的去满足。在最左侧的目标是f(_G34),即“想找到一个满足属性f的信息”。Prolog尝

试搜索自上而下搜索知识库。第一个找到能够和查询合一的是事实,f(a)。这会满足目标f(_G34),并且剩余另外两个目标。现在,当我们将f(_G34)和f(a)合一,_G34会被初始化为a,

而且这个初始化会将目标列表中所有的_G34都替换为a,所以剩余的目标列表看上去是这样的:

g(a), h(a)

当前的证明搜索图形如下:

但是g(a)是知识库中的事实,所以剩余目标列表中的第一个目标已经满足了,所以我们还剩下:

h(a)

证明搜索的图形如下:

但是无法再证明h(a),最后一个目标。因为关于属性h,我们通过知识库能够知道的唯一事实是,h(b),它无法和目标h(a)合一。

所以,接下来会发生什么?Prolog会承认犯了错误,并且检查是否在对目标进行合一时,有其他可能的值。通过上面图形展示的路径,Prolog会回到可以有合一替代的节点。

现在,在知识库中没有其他可以和g(a)合一的选择了,但是存在其他与f(_G34)合一的方式。存在多种合一可能的节点被称为选择节点。Prolog会保持对选择节点的追踪,所以

当它发现一种选择是错误的时候,能够回到选择节点并且尝试其他的路径。这个过程被称为回溯,它是Prolog证明搜索的基础。

那么继续我们的例子,Prolog回溯到选择节点。这个节点在上面图形中是下面的目标列表:

f(_G34), g(_G34), h(_G34).

Prolog必须重新开始工作,他必须尝试重新满足第一个目标。在知识库中,通过将事实f(b)和f(_G34)合一,可以满足目标。这个合一会将_G34初始化为b,所以剩余的目标列

表是:

g(b), h(b).

g(b)也是知识库中事实,也能够被满足,所以剩余:

h(b).

而且,这个也是知识库中的事实,所以这个目标也满足了。现在Prolog的目标列表已经为空。这意味着原始查询所需要的每一个目的都满足了,所以原始查询是能够成功的,并且,

Prolog也发现了为了达成目标而并且进行的初始化,即将变量Y初始化为b。

考虑当我们输入“;”查询是否有其他解决方案也很有趣:

这会强制Prolog进行回溯,去搜索是否有其他可能。但是上面的例子没有其他解决方案了,因为知识库中没有其他可能再去将h(b),g(b),f(_G34)或者k(Y)进行合一,所以Prolog

会回答false。另一方面,如果存在另外包含了k的规则,Prolog会按照我们之前描述的方式继续尝试,即在知识库中自上而下的搜索,从左自右地满足目标列表,如果有失败就回溯到

选择节点。

让我们看看整体的搜索过程,如下图:

这个图形是树的形式,事实上,这是我们第一个关于搜索树的例子。这种树的非叶子节点是为了满足证明搜索不同步骤的当前目标列表,树的边保存了为了满足当前目标而对知识库

中的事实或者规则进行合一的变量初始化信息(注意,当前目标是指目标列表的第一个即最左边的目标),叶子如果还包含没有被满足的目标,那么就是Prolog失败的点(错误的路径,

没有解决方案存在);叶子如果不包含任何的目标,就是一种可能的解决方案。从根节点到成功的叶子节点的路径,会给出为了满足原始目标而必须进行的变量初始化的值的全部信息。

接下来我们看另外一个例子,假设我们有如下的知识库:

loves(vincent, mia).
loves(marcellus, mia).

jealous(A, B) :- loves(A, C), loves(B, C).

如果我们查询:

?- jealous(X, Y).

对应的搜索树如下:

在知识库中只有一种针对jealous(X, Y)的合一方式,即使用如下的规则:

jealous(A, B) :- loves(A, C), loves(B, C).

所以我们的目标列表变成了如下:

loves(_G5, _G6), loves(_G7, _G6).

现在,我们需要在知识库中针对loves(_G5, _G6)进行合一。有两种方式可以做到(事实1和事实2),所以这也是一个选择节点。这两种情况,都是导致目标列表剩余loves(_G7, mia),

这个目标也可以通过知识库中的两个事实满足。最后所有的叶子节点都没有不能满足的目标,所以意味着一种有四种满足原始查询的解决方案。通过路径上的变量初始化信息,可以得出

如下的四种解决方案:

1. X = _G5 = vincent; Y = _G7 = vincent

2. X = _G5 = vincent; Y = _G7 = marcellus

3. X = _G5 = marcellus; Y = _G7 = marcellus

4, X = _G5 = marcellus; Y = _G7 = vincent

请仔细分析上面的例子,并确保能够完全理解。

时间: 2024-10-10 04:27:25

Learn Prolog Now 翻译 - 第二章 - 合一和证明搜索 - 第二节, 证明搜索的相关文章

Learn Prolog Now 翻译 - 第一章 - 事实,规则和查询 - 第二节, Prolog语法介绍

内容摘要: 原子(Atom) 数字(Numbers) 变量(Variables) 复杂语句(Complex Terms) 通过上一节的学习,我们已经大概熟悉了Prolog的编程思路,这一节我们会回过头,详细学习其中的一些语法细节.首先,问一个基础的问题:我们已经在Prolog程序中看到了很多类型的表达式 (比如,jody,playsAirGuitar(mia),和X),但这些仅仅只是例子,是时候更加深入了,到底事实.规则和查询是由什么构成的? 答案就是语句(terms),在Prolog中一共存在

Learn Prolog Now 翻译 - 第一章 - 事实,规则和查询 - 第一节, 一些简单的例子

 该系列文章是网上的Prolog学习资料:www.learnprolognow.org的中文翻译.希望能够通过翻译此学习资料,达到两个目的:第一.系统学习prolog的知识:第二.提升英文文章理解 和翻译能力. 内容摘要: 给出一些Prolog编程的简单例子: Prolog的基本结构:事实,规则和查询: 环境说明: 本系列文章使用的Prolog运行环境是:SWI-Prolog,官网地址是:http://www.swi-prolog.org. Prolog中只有三种基础结构:事实(facts),规

Learn Prolog Now 翻译 - 第一章 - 事实,规则和查询 - 第三节, 练习题和答案

练习题 1.1 下面的字符序列哪些是原子,哪些是变量,哪些两者都不是? 1. vINCENT 2. Footmassage 3. variable23 4. Variable2000 5. big_kahuna_burger 6. 'big kahuna burger' 7. big kahuna burger 8. 'Jules' 9. _Jules 10. '_Jules' 我的答案: vINCENT,variable23,big_kahuna_burger,'big kahuna burg

Learn Prolog Now 翻译 - 第二章 - 合一和证明查询树 - 第一节, 合一

内容提要: 合一的定义: 一些合一的例子: 触发校验: 使用合一编程: 合一的定义 在上一章的知识库KB4中,我们简单地提及了合一的思想.比如,Prolog将woman(X)和woman(mia)合一,所以把变量X初始化为mia.现在是时候更加细致地研究合一,因为合一是 Prolog中最为基础的思想. 回顾一下Prolog中的三种语句类型: 1. 常量,可能是原子(比如vincent)或者是数字(比如24). 2. 变量,比如X,Z3,List等. 3. 复杂语句,形式为:functor(ter

Learn Prolog Now 翻译 - 第三章 - 递归 - 第一节,递归的定义

在Prolog中,谓词可以递归地定义.简要地讲,一个谓词是递归定义的,如果一个或者多个规则的定义中包含了规则自身. 例子1:消化 考虑如下的知识库: is_digesting(X, Y) :- just_ate(X, Y). is_digesting(X, Y) :- just_ate(X, Z), is_digesting(Z, Y). just_ate(mosquito, blood(john)). just_ate(frog, mosquito). just_ate(stork, frog

Learn Prolog Now 翻译 - 第二章 - 合一和证明搜索 - 第三节, 练习题和答案

练习题 2.1 下面各组语句中,哪些组是能够合一的?如果能够合一,请给出变量初始化的信息. 1. bread = bread. 2. 'Bread' = bread. 3. 'bread' = bread. 4. Bread = bread. 5. bread = sausage. 6. food(bread) = bread. 7. food(bread) = X. 8. food(X) = food(bread). 9. food(bread, X) = food(Y, sausage).

Learn Prolog Now 翻译 - 第四章 - 列表 - 第二节,列表成员

内容提要 本章主要介绍使用递归操纵列表的一个实际例子:判断一个元素是否在包含在一个列表中. 是时候介绍第一个Prolog中通过递归操纵列表的程序例子了.我们最感兴趣的事情之一是,某个对象是否是列表中的元素.所以,我们想写一个程序,当假设输入是一个对象X和一个列表L, 得出结果是X是否属于L.这个程序的名字通常是:member,是Prolog程序中使用递归操纵列表最简单的例子,如下: member(X, [X|T]). member(X, [H|T]) :- member(X, T). 这就是全部

Learn Prolog Now 翻译 - 第五章 - 数字运算 - 第二节,数字运算与列表

内容提要 列表中的一些数字运算,累加器 尾递归调用 列表中的一些数字运算,累加器 关于数字运算最为重要的应用,可能是获取一些数据结构体的一些有用事实,比如列表.例如,知道列表的长度是很有用的.我们将会给出一些使用列表和数字运算的例子. 一个列表的长度是多少?这里有一个递归定义: 1. 空列表的长度为0. 2. 非空列表的长度为 1 + len(T),其中len(T)是非空列表的尾部. 这个定义在Prolog中很容易实现,以下是实现代码: len([], 0). len([_|T], N) :-

Learn Prolog Now 翻译 - 第三章 - 递归 - 第二节,规则顺序,目标顺序,终止

内容提要 规则顺序 目标顺序 终止 Prolog是第一门比较成功的逻辑编程语言.逻辑编程语言内在实现是简单和富有魅力的:程序员的工作简单地说就是描述问题:程序员应该写下(使用语言的逻辑)声明性的规格说明 (即,一个知识库),去描述有趣的状态.事实和关系:程序员不应该告诉计算机如何去实现,而他根据问一些问题去获取信息,逻辑编程语言会给出答案. 然而,以上是理想情况,Prolog本身也确实通过一些重要的特征,往这个方向在努力.但是Prolog不是,重复一次,不是一门完整的逻辑编程语言.如果你只是从声