逻辑型程序设计语言PROLOG详细教程.docx
《逻辑型程序设计语言PROLOG详细教程.docx》由会员分享,可在线阅读,更多相关《逻辑型程序设计语言PROLOG详细教程.docx(39页珍藏版)》请在冰豆网上搜索。
逻辑型程序设计语言PROLOG详细教程
逻辑型程序设计语言PROLOG详细教程(总22页)
逻辑型程序设计语言PROLOG教程
.1逻辑型程序设计语言PROLOG
PROLOG的语句
PROLOG语言只有三种语句,分别称为事实、规则和问题。
1.事实(fact)
格式:
<谓词名>(<项表>).
功能一般表示对象的性质或关系。
其中谓词名是以小写英文字母打头的字母、数字、下划线等组成的字符串,项表是以逗号隔开的项序列。
例如:
student(john).
like(mary,music).
表示“约翰是学生”和“玛丽喜欢音乐”。
2.规则(rule)
格式:
<谓词名>(<项表>):
-<谓词名>(<项表>){,<谓词名>(<项表>)}.
功能:
一般表示对象间的因果关系、蕴含关系或对应关系。
其中“:
-”号表示“if”(也可以直接写为if),其左部的谓词是规则的结论(亦称为头),右部的谓词是规则的前提(亦称为体),{}表示零次或多次重复,逗号表示and(逻辑与),即规则的形式是一个逻辑蕴含式。
例如:
bird(X):
-animal(X),has(X,feather).
grandfather(X,Y):
-father(X,Z),father(Z,Y).
第一条规则表示“如果X是动物,并且X有羽毛,则X是鸟”;第二条规则就表示“X是Y的祖父,如果存在Z,X是Z的父亲并且Z又是Y的父亲”。
3.问题(question)
格式:
-<谓词名>(<项表>){,<谓词名>(<项表>)}.
功能表示用户的询问,它就是程序运行的目标。
例如:
-student(john).
-like(mary,X).
.2PROLOG程序
PROLOG程序一般由一组事实、规则和问题组成。
问题是程序执行的起点,称为程序的目标。
例如下面就是一个PROLOG程序。
likes(bell,sports).
likes(mary,music).
likes(mary,sports).
likes(jane,smith).
friend(john,X):
-likes(X,reading),likes(X,music).
friend(john,X):
-likes(X,sports),likes(X,music).
-friend(john,Y).
可以看出,这个程序中有四条事实、两条规则和一个问题。
其中事实、规则和问题都分行书写。
规则和事实可连续排列在一起,其顺序可随意安排,但同一谓词名的事实或规则必须集中排列在一起。
问题不能与规则及事实排在一起,它作为程序的目标要么单独列出,要么在程序运行时临时给出。
这个程序的事实描述了一些对象(包括人和事物)间的关系;而规则则描述了john交朋友的条件,即如果一个人喜欢读书并且喜欢音乐(或者喜欢运动和喜欢音乐),则这个人就是john的朋友(当然,这个规则也可看作是john朋友的定义);程序中的问题是“约翰的朋友是谁”
当然,PROLOG程序中的目标可以变化,也可以含有多个语句(上例中只有一个)。
如果有多个语句,则这些语句称为子目标。
例如对上面的程序,其问题也可以是
-likes(mary,X).
或
-likes(mary,music).
或
-friend(X,Y).
或
-likes(bell,sports),likes(mary,music),friend(john,X).
等等。
当然,对于不同的问题,程序运行的结果一般是不一样的。
PROLOG程序的运行机理
PROLOG程序的运行是从目标出发,并不断进行匹配、合一、归结,有时还要回溯,直到目标被完全满足或不能满足时为止。
1.自由变量与约束变量
PROLOG中称无值的变量为自由变量,有值的变量为约束变量。
一个变量取了某值就说该变量约束于某值,或者说该变量被某值所约束,或者说该变量被某值实例化了。
2.匹配合一
两个谓词可匹配合一,是指两个谓词的名相同,参量项的个数相同,参量类型对应相同,并且对应参量项还满足下列条件之一:
(1)如果两个都是常量,则必须完全相同。
(2)如果两个都是约束变量,则两个约束值必须相同。
(3)如果其中一个是常量,一个是约束变量,则约束值与常量必须相同。
(4)至少有一个是自由变量。
例如:
下面的两个谓词
pre1("ob1","ob2",Z)
pre1("ob1",X,Y)
只有当变量X被约束为"ob2",且Y、Z的约束值相同或者至少有一个是自由变量时,它们才是匹配合一的。
3.回溯
所谓回溯,就是在程序运行期间,当某一个子目标不能满足(即谓词匹配失败)时,控制就返回到前一个已经满足的子目标(如果存在的话),并撤消其有关变量的约束值,然后再使其重新满足。
成功后,再继续满足原子目标。
如果失败的子目标前再无子目标,则控制就返回到该子目标的上一级目标(即该子目标谓词所在规则的头部)使它重新匹配。
回溯也是PROLOG的一个重要机制。
下面,我们介绍PROLOG程序的运行过程。
我们仍以上面的程序为例。
设所给的询问是
-friend(john,Y).(john和谁是朋友)
则求解目标为
friend(john,Y).
这时,系统对程序进行扫描,寻找能与目标谓词匹配合一的事实或规则头部。
显然,程序中前面的四条事实均不能与目标匹配,而第五个语句的左端即规则
friend(john,X):
-likes(X,reading),likes(X,music).
的头部可与目标谓词匹配合一。
但由于这个语句又是一个规则,所以其结论要成立则必须其前提全部成立。
于是,对原目标的求解就转化为对新目标
likes(X,reading),likes(X,music).
的求解。
这实际是经归结,规则头部被消去,而目标子句变为
-likes(X,reading),likes(X,music).
现在依次对子目标
likes(X,reading)和likes(X,music)
求解。
子目标的求解过程与主目标完全一样,也是从头对程序进行扫描,不断进行测试和匹配合一等,直到匹配成功或扫描完整个程序为止。
可以看出,对第一个子目标like(X,reading)的求解因无可匹配的事实和规则而立即失败,进而导致规则
friend(john,X):
-likes(X,reading),likes(X,music).
的整体失败。
于是,刚才的子目标
likes(X,reading)和likes(X,music)
被撤消,系统又回溯到原目标friend(john,X)。
这时,系统从该目标刚才的匹配语句处(即第五句)向下继续扫描程序中的子句,试图重新使原目标匹配,结果发现第六条语句的左部,即规则
friend(john,X):
-likes(X,sports),likes(X,music).
的头部可与目标为谓词匹配。
但由于这个语句又是一个规则,于是,这时对原目标的求解,就又转化为依次对子目标
likes(X,sports)和likes(X,music)
的求解。
这次子目标likes(X,sports)与程序中的事实立即匹配成功,且变量X被约束为bell。
于是,系统便接着求解第二个子目标。
由于变量X已被约束,所以这时第二个子目标实际上已变成了
likes(bell,music).
由于程序中不存在事实likes(bell,music),所以该目标的求解失败。
于是,系统就放弃这个子目标,并使变量X恢复为自由变量,然后回溯到第一个子目标,重新对它进行求解。
由于系统已经记住了刚才已同第一子目标谓词匹配过的事实的位置,所以重新求解时,便从下一个事实开始测试。
易见,当测试到程序中第三个事实时,第一个子目标便求解成功,且变量X被约束为mary。
这样,第二个子目标也就变成了
likes(mary,music).
再对它进行求解。
这次很快成功。
由于两个子目标都求解成功,所以,原目标friend(john,Y)也成功,且变量Y被约束为mary(由Y与X的合一关系)。
于是,系统回答:
Y=mary
程序运行结束。
上面只给出了问题的一个解。
如果需要和可能的话,系统还可把john的所有朋友都找出来。
我们把上述程序的运行过程再用示意图(图2─1)描述如下:
图2─1PROLOG程序运行机理示例
上述程序的运行是一个通过推理实现的求值过程。
我们也可以使它变为证明过程。
例如,把上述程序中的询问改为
friend(john,mary)
则系统会回答:
yes
若将询问改为:
riend(john,smith)
则系统会回答:
no
从上述程序的运行过程可以看出,PROLOG程序的执行过程是一个(归结)演绎推理过程。
其特点是:
推理方式为反向推理,控制策略是深度优先,且有回溯机制。
其具体实现方法是:
匹配子句的顺序是自上而下;子目标选择顺序是从左向右;(归结后)产生的新子目标总是插入被消去的目标处(即目标队列的左部)。
PROLOG的这种归结演绎方法被称为SLD(LinearresolutionwithSelectionfunctionforDefiniteclause)归结,或SLD反驳-消解法。
SLD归结就是PROLOG程序的运行机理,它也就是所谓的PROLOG语言的过程性语义。
TurboPROLOG程序设计
TurboPROLOG的程序结构
一个完整的TurboPROLOG版)程序一般包括常量段、领域段、数据库段、谓词段、目标段和子句段等六个部分。
各段以其相应的关键字constants、domains、database、predicates、goal和clauses开头加以标识。
:
另外,在程序的首部还可以设置指示编译程序执行特定任务的编译指令;在程序的任何位置都可设置注解。
总之,一个完整的TurboPROLOG版)程序的结构如下
/*<注释>*/
<编译指令>
constants
<常量说明>
domains
<域说明>
database
<数据库说明>
predicates
<谓词说明>
goal
<目标语句>
clauses
<子句集>
当然,一个程序不一定要包括上述所有段,但一个程序至少要有一个predicates段、clauses段和goal段。
在大多数情形中,还需要一个domains段,以说明表、复合结构及用户自定义的域名。
如若省略goal段,则可在程序运行时临时给出,但这仅当在开发环境中运行程序时方可给出。
若要生成一个独立的可执行文件,则在程序中必须包含goal段。
另一方面,一个程序也只能有一个goal段。
例如果把上节中的程序要作为TurboPROLOG程序,则应改写为:
/*例子程序-1*/
DOMAINS
name=symbol
PREDICATES
likes(name,name).
friend(name,name)
GOAL
friend(john,Y),write(″Y=″,Y).
CLAUSES
likes(bell,sports).
likes(mary,music).
likes(mary,sports).
likes(jane,smith).
friend(john,X):
-likes(X,sports),likes(X,music).
friend(john,X):
-likes(X,reading),likes(X,music).
结合上例,我们再对上述程序结构中的几个主要段的内容和作用加以说明(其余段在后面用到时再作说明):
领域段该段说明程序谓词中所有参量项所属的领域。
领域的说明可能会出现多层说明,直到最终说明到TurboPROLOG的标准领域为止(如上例所示)。
TurboPROLOG的标准领域即标准数据类型,包括整数、实数、符号、串和符号等,其具体说明如表所示。
表TurboPROLOG的标准领域
谓词段:
该段说明程序中用到的谓词的名和参量项的名(但TurboPROLOG的内部谓词无须说明)。
子句段:
该段是TurboPROLOG程序的核心,程序中的所有事实和规则就放在这里,系统在试图满足程序的目标时就对它们进行操作。
目标段:
该段是放置程序目标的地方。
目标段可以只有一个目标谓词,例如上面的例子中就只有一个目标谓词;也可以含有多个目标谓词,如:
goal
readint(X),Y=X+3,write("Y=",Y).
就有三个目标谓词。
这种目标称为复合目标。
另外,一般称程序目标段中的目标为内部目标,而称在程序运行时临时给出的目标为外部目标。
TurboPROLOG的数据与表达式
1.领域
1)标准领域
TurboPROLOG中不定义变量的类型,只说明谓词中各个项的取值域。
2)结构
结构也称复合对象,它是TurboPROLOG谓词中的一种特殊的参量项(类似于谓词逻辑中的函数)。
结构的一般形式为
<函子>(<参量表>)
其中函子及参量的标识符与谓词相同。
注意,这意味着结构中还可包含结构。
所以,复合对象可表达树形数据结构。
例如下面的谓词
likes(Tom,sports(football,basketball,table-tennis)).
中的
sports(football,basketball,table-tennis)
就是一个结构,即复合对象。
又如:
person("张华",student("西安石油学院"),address("中国","陕西","西安")).
reading("王宏",book("人工智能技术基础教程","西安电子科技大学出版社")).
friend(father("Li"),father("Zhao")).
这几个谓词中都有复合对象。
复合对象在程序中的说明,需分层进行。
例如,对于上面的谓词
likes(Tom,sports(football,basketball,table-tennis)).
在程序中可说明如下:
domains
name=symbol
sy=symbol
sp=sports(sy,sy,sy)
predicates
likes(name,sp)
3)表
表的一般形式是
[x1,x2,…,xn]
其中xi(i=1,2,…,n)为PROLOG的项,一般要求同一个表的元素必须属于同一领域。
不含任何元素的表称为空表,记为[]。
例如下面就是一些合法的表。
[1,2,3]
[apple,orange,banana,grape,cane]
["PROLOG","MAENS","PROGRAMMING","inlogic"]
[[a,b],[c,d],[e]]
[]
表的最大特点是其元素个数可在程序运行期间动态变化。
表的元素也可以是结构或表,且这时其元素可以属于不同领域。
例如:
name("LiMing"),age(20),sex(male),address(xian)]
[[1,2],[3,4,5],[6,7]]
都是合法的表。
后一个例子说明,表也可以嵌套。
实际上,表是一种特殊的结构。
它是递归结构的另一种表达形式。
这个结构的函数名取决于具体的PROLOG版本。
这里我们就用一个圆点来表示。
下面就是一些这样的结构及它们的表表示形式:
结构形式表形式
·(a,[])[a]
·(a,·(b,[]))[a,b]
·(a,·(b,·(c,[])))[a,b,c]
表的说明方法是在其组成元素的说明符后加一个星号*。
如:
domains
lists=string*
predicates
pl(lists)
就说明谓词pl中的项lists是一个由串string组成的表。
对于由结构组成的表,至少得分三步说明。
例如对于下面谓词p中的表
p([name("Liming"),age(20)])
则需这样说明:
domains
rec=seg*
seg=name(string);age(integer)
predicates
p(rec)
2.常量与变量
由上面的领域可知,TurboPROLOG的常量有整数、实数、字符、串、符号、结构、表和文件这八种数据类型。
同理,TurboPROLOG的变量也就有这八种取值。
另外,变量名要求必须是以大写字母或下划线开头的字母、数字和下划线序列,或者只有一个下划线。
这后一种变量称为无名变量。
3.算术表达式
TurboPROLOG提供了五种最基本的算术运算:
加、减、乘、除和取模,相应运算符号为+、-、*、/、mod。
这五种运算的顺序为:
*、/、mod优先于+、-。
同级从左到右按顺序运算,括号优先。
算术表达式的形式与数学中的形式基本一样。
例如:
数学中的算术表达式PROLOG中的算术表达式
x+yzX+Y*Z
ab-c/dA*B-C/D
umodvUmodV(表示求U除以V所得的余数)
即是说,TurboPROLOG中算术表达式采用通常数学中使用的中缀形式。
这种算术表达式为PROLOG的一种异体结构,若以PROLOG的结构形式来表示,则它们应为
+(X,*(Y,Z))
-(*(A,B),/(C,D))
mod(U,V)
所以,运算符+、-、*、/和mod实际也就是PROLOG内部定义好了的函数符。
在TurboPROLOG程序中,如果一个算术表达式中的变元全部被实例化(即被约束)的话,则这个算术表达式的值就会被求出。
求出的值可用来实例化某变量,也可用来同其他数量进行比较,用一个算术表达式的值实例化一个变量的方法是用谓词“is”或“=”来实现。
例如:
YisX+5或Y=X+5(*)
就使变量Y实例化为X+5的值(当然X也必须经已被某值实例化),可以看出,这里对变量Y的实例化方法类似于其他高级程序语言中的“赋值”,但又不同于赋值。
例如,在PROLOG中下面的式子是错误的:
X=X+1
4.关系表达式
TurboPROLOG提供了六种常用的关系运算,即小于、小于或等于、等于、大于、大于或等于和不等于,其运算符依次为
<,<=,=,>,>=,<>
TurboPROLOG的关系表达式的形式和数学中的也基本一样,例如:
数学中的关系式TurboPROLOG中的关系式
X+1≥YX+1>=Y
X≠YX<>Y
即是说,TurboPROLOG中的关系式也用中缀形式。
当然,这种关系式为TurboPROLOG中的异体原子。
若按TurboPROLOG中的原子形式来表示,则上面的两个例子为
>=(X+1,Y)和<>(X,Y)
所以上述六种关系运算符,实际上也就是TurboPROLOG内部定义好了的六个谓词。
这六个关系运算符可用来比较两个算术表达式的大小。
例如:
brother(Name1,Name2):
-person(Name1,man,Age1),
person(Name2,man,Age2),
mother(Z,Name1),mother(Z,Name2),
Age1>Age2.
需要说明的是,“=”的用法比较特殊,它既可以表示比较,也可以表示约束值,即使在同一个规则中的同一个“=”也是如此。
例如:
(例一)
p(X,Y,Z):
-Z=X+Y.
当变量X、Y、Z全部被实例化时,“=”就是比较符。
如:
对于问题
Goal:
p(3,5,8).
机器回答:
yes。
而对于
Goal:
p(3,5,7).
机器回答:
no。
即这时机器把X+Y的值,与Z的值进行比较。
(例二)
但当X,Y被实例化,为Z未被实例化时,“=”号就是约束符。
如:
Goal:
p(3,5,Z).
机器回答:
Z=8
这时,机器使Z实例化为X+Y的结果。
输入与输出
虽然PROLOG能自动输出目标子句中的变量的值,但这种输出功能必定有限,往往不能满足实际需要;另一方面,对通常大多数的程序来说,运行时从键盘上输入有关数据或信息也是必不可少的。
为此每种具体PROLOG一般都提供专门的输入和输出谓词,供用户直接调用。
例如,下面就是TorboPROLOG的几种输入输出谓词:
(1)readln(X)。
这个谓词的功能是从键盘上读取一个字符串,然后约束给变量X。
(2)readint(X)。
这个谓词的功能是从键盘上读取一个整数,然后约束给变量X,如果键盘上打入的不是整数则该谓词失败。
(3)readreal(X)。
这个谓词的功能是从键盘上读取一个实数,然后约束给变量X,如果键盘上打入的不是实数则该谓词失败。
(4)readchar(X)。
这个谓词的功能是从键盘上读取一个字符,然后约束给变量X,如果键盘上打入的不是单个字符,则该谓词失败。
(5)write(X1,X2,… Xn)。
这个谓词的功能是把项Xi(i=1,2,…n)的值显示在屏幕上或者打印在纸上,当有某个Xi未实例化时,该谓词失败,其中的Xi可以是变量,也可以是字符串或数字。
(6)nl换行谓词。
它使后面的输出(如果有的话)另起一行。
另外,利用write的输出项"\n"也同样可起换行作用。
例如:
write("name"),nl,write("age")
与write("name","\n","age")的效果完全一样。
例用上面的输入输出谓词编写一个简单的学生成绩数据库查询程序。
PREDICATES
student(integer,string,real)
grade
GOAL
grade.
CLAUSES
student(1,"张三",.
student(2,"李四",.
student(3,"王五",.
grade:
-write("请输入姓名:
"),readln(Name),
student(-,Name,Score),
nl,write(Name,"的成绩是",Score).
grade:
-write(“对不起,找不到这个学生!
”).
grade:
-write("对不起,找不到这个学生!
").
下面是程序运行时的屏幕显示:
请输入姓名:
王五
王五的成绩是。
分支与循环
PROLOG中并无专门的分支和循环语句,但PROLOG也可实现分支和循环程序结构。
1.分支
对于通常的IF-THEN-ELSE分支结构,PROLOG可用两条同头的并列规则实现。
例如,将
IFx>0THENx:
=1
ELSEx:
=0
用PROLOG实现则是
Br:
-x>0,x=1.
Br:
-x=0.
类似地,对于多分支,可以用多条规则实现。
例如:
Br:
-x>0,x=1.
Br:
-x=0,x=0.
Br:
-x<0,x=-1.
2.循环
PROLOG可以实现计循环次数的FOR循环,也可以实现不计循环次数的DO循环。
例如下面的程序段就实现了循环,它使得write语句重复执行了三次,而打印输出了三个学生的记录。
student(1,"张三",.
student(2,"李四",.
student(3,"王