- Prolog的程序是由一系列的事实和规则组成的数据库。
- 规则之间的调用是通过联合操作完成的,Prolog能够自动的完成模式匹配。
- 规则还可以调用内部谓词,例如write/1。
- 我们可以在Prolog的解释器中单独地对规则进行查询(调用)。
在Prolog的程序的运行流程方面我有了如下的认识:
- 规则的运行是通过Prolog内建的回溯功能实现的。
- 我们可以使用内部谓词fail来强制实现回溯。
- 我们也可以通过加入一条参数为伪变量(下划线)无Body部分的子句,来实现强制让谓词成功。
我们还学习了,
- 数据库中的事实代替了一般语言中的数据结构。
- 回溯功能能够完成一般语言中的循环操作。
- 而通过模式匹配能够完成一般语言中的判断操作。
- 规则能够被单独地调试,它和一般语言中的模块相对应。
- 而规则之间的调用和一般语言中的函数的调用类似。
有了以上的知识,我们还可以编写出一些让其它语言的程序员吃惊的小程序。下面就举一个分析家谱的程序。
假如我们把家族成员之间的父子关系和夫妻关系,以及成员的性别属性定义为基本的事实数据库,我们就可以编出许多规则来判断其他的亲戚关系了。
例如我们有如下的数据库:
father(a,b).
father(a,d).
father(a,t).
father(b,c).
wife(aw,a).
wife(bw,b).
male(t).
female(d).
male(c).
father(a,b).表示a是b的父亲。
wife(aw,a). 表示aw是a的妻子。
male(t).表示b是男性。
female(d).表示d是女性。
上面我们并没有定义a、b、aw、bw的性别。 因为通过他们和其他人的关系我们可以很容易地确定他们的性别。不过要想让Prolog知道他们的性别我们就要定义如下的规则。
male(X):-father(X,_).
female(X):-wife(X,_).
上面的male/1和female/1的谓词名称和事实的名称相同,这并不是什么特别的情况,你可以把所有定义相同的谓词的子句之间的关系想象“或者”的关系。也就是说:t和d是男性,或者如果X是其他人的父亲,则它也是男性。在判断性别时,我们并不关心此人是谁的父亲,所以后面一个变量用“_”代替了。
好了,假如有如下的询问:
?-male(t).
yes.
?-male(a).
yes.
?-male(X).
X=t;
X=c;
X=a;
X=a;
X=a;
X=b;
no.
最后一个询问,它虽然把所有的男性找了出来,可是它把a找了三次,原因很简单,因为我们有三个father/2的子句都包含a,好像不太理想,不过现在只能将就一下了,当我们学习了更多的知识后,就好解决了。
下面我们定义一些其他的亲戚关系的规则。你大概一看就能够理解。例如:X和Y是兄弟的条件是: X和Y有相同的父亲{father(Z,X),father(Z,Y)},并且他们都是男性{male(X),male(Y)},最后由于X和Y可以取相同的值,所以我们不得不加上一条X和Y不是同一个人{X/=Y}。
grandfather(X,Y):-father(X,Z),father(Z,Y).
mother(X,Y):-wife(X,Z),father(Z,Y).
brother(X,Y):-father(Z,X),father(Z,Y),male(X),male(Y),X/=Y.
当然我们还可以加入更复杂一点的规则,
uncle(X,Y):-brother(X,Z),father(Z,Y).
这个叔伯的规则uncle/2调用了前面的规则brother/2。
这里只是简单回顾一下前面所学习的知识,所以这个家族程序虽然可以使用,但是却极不完善。例如:它会把某一答案重复多次,还不能描述没有小孩的丈夫的性别。 我们这样改一下会更好一点:male(X):-wife(_,X)。因此,规则的定义是多种多样的,到底哪种更好、哪种更快,这就是我们以后所要研究的问题之一了。