likes(john,X) :- person(X), likes(X, talk),likes(X, swim). 若要表示父母和(外)祖父母等关系,可用下列规则: parent(X, Y):—mother(X, Y). parent(X, Y):—father(X, Y).
grandparent(X, Y):—parent(X, Z), parent(Z, Y). 5.询问
我们可以对一些事实询问,在PROLOG中一个问题很象一个事实,只要在它前面加一特殊符号“?—”。例如:
?—owns(mary, book)
其意思是Mary有这本书吗?或Mary有这本书是真的吗?但不能问她是否有所有的书。
当询问时,PROLOG要搜索事先输入的事实组成的数据库,看是否有事实与问题相匹配。当事实与询问的关系相同且相应的自变时也相同时,我们说二者相匹配。如果PROLOG找到一个事实与问题相匹配,则回答“是”;否则,数据库里无此事实则回答“否”,上面询问中的owns(mary,book), 又称为目标,或者说询问自由目标组成。
假设已有如下事实。 on(book, desk). owns(john, book). female(jane).
play(jane,jin, badminton). valuable(gold) 则可以问:
?—female(jane).
yes
?—owns(john, moon).
no
这里下划线的部分表示PROLOG回答的信息(以下同)。
?—male(tom).
no
对前面两个问题的回答是清楚的。对第三个问题,PROLOG也回答no,这是因为数据库中没有这样的事实,这个no意味着“就我所知,这是不对的。”因此系统回答no有两种意思:不或者不知道。
显然,仅仅询问这样的问题。获得yes和no的回答是不够的。询问象“John有什么东西?”,“谁与谁打羽毛球?”也是用户常提出的问题,但这些问题必须借助变量来提出。如下所示:
?—owns(john, X)
X=book
?—play(X, Y, badminton)
X=jane, Y=jin ?—owns(X, book).
X=john
在介绍规则时,我们曾引入“连结”的概念,即用逗号“,”把一规则体中的多个目标连结在一起,这里“,”表示“并且”。
同样,在询问中也可以将几个目标用“,”连续起来放在“?—”的后面,以便我们问更复杂关系的问题,如:
?—play(X, jin, badminton),female(X). X=jane.
至此,我们已经介绍了PROLOG基本的核心内容,主要是: ①说明有关对象的事实。 ②用规则的形式表示关系。 ③问有关事实和规则的问题。 ④用大写字母开关的字符串表示变量。 ⑤用连结符号“,”表示并且。
二、数据结构和递归
1.项
PROLOG提供一个一致的数据结构称为项,所有的数据和PROLOG程序都是由项构造而成的。PROLOG中项的定义用BNF形式可写成;
<项>∷=<常量>|<变量>|<结构>|<(项)>|
项可以是常量、变量、结构或者括在括号里的项。在前面,所有这些项我们都已经碰到,只是不熟悉它们的名称。
(1)常量
原子或者是整数,即<常量>∷=<原子>|<整数>。
原子用来标识对象的名字、谓词(对象间关系)等。原子是以小写字母开头的字母数字串,中间可插有下划线符号“—”或仅由符号组成。整数用来表示数,使得PROLOG能执行算术运算。整数由数字组成,不包含小数点。
(2)变量
用来表示暂时不能命名或不需要命名的对象,变量是以大写字母或下划线符号“—”开头的字母数字串,中间可插有“—”。无名变量表示为“—”,因它的名字从来不会被使用。、
在同一个子句中的几个无名变量,不需要给出一致的解释。这是无名变量的特有性质,它与在同一个子句中,一个有名变量的多次出现要有一致的解释不同。
(3)结构
或称复合项。它是由一组其它对象(也可为结构)组成的单个对象,这些其它对象称为它的成分,把几个成分组合成一个结构作为单个对象是很有用处的,结构的定义的;
<结构>∷=<函数符>(<项>{,<项>}) <函数符>∷=<原子> 下面是一些结构的例子: owns(john, book)
owns(john,book(prolog, author(clocksin, Melish)) a (b, c, d) 3+5*2 gcd(X, f(X))
其中第四个例子是结构+(3,*(5,2))的另一种表示。为对应这种形式,有人在结构定义中加入<项><原子><项>{<原子><项>},这样第四个例子中3是项,+ 是原子,5是项,*是原子,2是项,其它四个例子均符合定义中的<函数符>(<项>{,<项>})。
若把一个复杂的结构表示成一棵树将更容易理解。例如,把上面例子中每一
个函数符(如owns,author等)看作为根(或子根),它的变元为分枝,而每一个分枝又可指向另一个结构,这样就有图6-9的树形结构。
2.表和它的递归性
表是非数值程序设计中一种最常用的数据结构。LISP与PROLOG都以此作为它们的数据结构。所不同的是,在PROLOG中表仅为一特殊类的结构;而在LISP中表是唯一的数据结构。表是元素的有序序列,有序指序列中元素的次序是重要的。PROLOG中,一个表的元素可以是原子,结构或任何其它项包括其它表,因此表是递归定义的。事实上,表能够表示人们在符号处理中希望使用的任一类结构。
PROLOG中的表或者是一个没有元素的空表,或者是一个结构,它包括头和尾两个成分,空表写成[],非空表写·(头,尾),这里“·”是一个函数符。表的头和尾是它的两个成分(变元)。当尾没有元素时,尾写为空表。例如由元素a组成的表为:·(a,[])。
同样,由原子a,b,c组成的表: ·(a,·(b,·(c,[])))
由此可见,表能表示成二元树。
用点表示法写复杂的表极不方便,而且难看,因此PROLOG也用称为表表示法的方法未写表。表表示法是用逗号把表的诸元素分开,并且把所有元素括在方括号内,例如上面两个表用表表示法可写成为[a]和[a,b,c]。
根据表表示法和点表示的意义,[a,b,c]可写成·(a,[b,c]),进一步写成·(a,·(b,[c]))以及·(a,·(b, ·(c,[])))。在表中包含其它表或变量是十分有用的。例如下面的表在PROLOG中最合法的:
[]空表 [a,b,c,[d,e,f],g] [a,V1,b,[X,Y]]
表表示法是PROLOG实际使用的表示形式、表的大小是最外层元素的个数。因此上面三个表的大小分别为0,5,4。
对表的操作是通过把表分成头和尾来进行的,在表表示法中,头是表中最外层的第一个元素,表的尾是一个表,这个表由原来表的除了第一个元素外的所有元素组成。
为了对表进行取表头和取表尾的操作,在PROLOG中有一种专门表示法来
表示一个分明头X和尾Y的表,它写成[X |Y],这里分隔X和Y的符号是垂杠“|”。当这个模式匹配任一表时,PROLOG将把X实例化为匹配的表的头,Y实例化为该表的尾,例如,表[a,b,c.d]的[X|Y]形式为[a|[b,c,d]],所以X=a, Y=[b,c,d]因为[a,b,c,d]的表头为a,表尾为[b,c,d],同样,表[a]的[X|Y]形式为[a|[ ] ],所以X=a,Y=[ ],再看下面一些例子。
若有p([1,2,3]).
p([the,cat,sat,[on,the,mat]]). 于是?—p([X|Y])
X=1,Y=[2, 3]; / *打;号*/ – – – – – – – – – – – – – – X=the, Y=[cat,sat,[on,the,mat]]
– – – – – – – – – – – – – – – – – – – – – – – – – ?—p([_, _, _,[_|X]].
X=[the,mat] – – – – – – – – – – – – ?—p([X, Y|Z]).
X=1, Y=2,Z=[3]; – – – – – – – – – – – – – – X=the, Y=cat, Z=[sat,[on,the,mat]] – – – – – – – – – – – – – – – – – – – – 3. 美术运算
LISP和PROLOG均不得以数值计算为其目标的,它们主要用于非数值程序设计。因此,在算术运算方面它们不能与传统的FORTRAN,PASCAL相比拟,它们只提供了一些基本的运算,PROLOG提供的五种最基本的算术运算是:加、减、乘、除和取模,写成一般的中级形式为XopY,其中OP是算术运算符,可为+ ,-,*,/,mod,/是指整数除,即X、Y为整数,X/Y是X除以Y的商(整数)。Xmod Y是X除以Y的余数。
算术运算符的优先级与通常数学上的规定相同,即*,/,mod高于+,-。 所以:
X+Y * Z X+(Y * Z) 两者一样