数据与表达式
1、领域
(1)标准领域.
Turbo prolog中不定义变量的类型,只说明谓词中各个项的取值域.由上面我们知道, Turbo prolog 有整数、实数、字符、串 和 符号这5种标准域.另外,它还有结构、表和文件这3种复合域.
(2)结构.
结构也称复合对象,它是 Turbo prolog 谓词中的一种特殊的参量项(类似于谓词逻辑中的函数).结构的一般形式为:
《函子》(《参量表》)1
其中函子及参量的标识符与谓词相同.注意,这意味着结构中还可包含结构.所以,复合对象可表达树形数据结构.例如下面的谓词:
likes(“Tom”, sports(football, basketball, table_tennis)).1
中的
sports(football, basketball, table_tennis)1
就是一个结构,即复合对象.又如:
person(“张华”, student(“清华大学”), address(“中国”, “北京”)).
reading(“王宏”, book(“人工智能技术导论”, “西安电子科技大学出版社”)).
friend(father(“Li”), father(“Zhao”)).123
这几个谓词中都有复合对象.
复合对象在程序中的说明,需分层进行.例如,对于上面的谓词:
likes(“Tom”, sports(football, basketball, table_tennis)).1
在程序中可说明如下:
domains
name = symbol
sy = symbol
sp = sports(sy, sy, sy)
predicates
likes(name, sp)123456
(3)表.
表的一般形式是:
[x1, x2, ..., xn]1
其中xi(i=1,2,...,n)为 Prolog 的项,一般要求同一个表的元素必须属于同一领域.不含任何元素的表称为空表,记为[].例如下面就是一些合法的表.
[1,2,3]
[ apple, orange, banana, grape, cane]
[“Prolog”, “MAENS”, “PROGRAMMING”, “in logic”]
[[a, b], [c, d], [e]]
[]12345
表的最大特点是其元素个数可在程序运行期间动态变化.表的元素也可以是结构或表,且这时其元素可以属于不同领域.例如:
[name(“LiMing”), age(20), sex(male), address(xian)]
[[1, 2], [3, 4, 5], [6, 7]]12
都是合法的表.后一个例子说明,表也可以嵌套.
实际上,表是一种特殊的结构,它是递归结构的另一种表达形式.这个结构的函数名取决于具体的 Prolog 版本,这里我们就用一个圆点来表示.下面就是一些这样的结构及它们的表的表示形式:
结构形式表形式
·(a, [])[a]
·(a, ·(b, []))[a, b]
·(a, ·(b, ·(c, [])))[a, b, c]
表的说明方法是在其组成元素的说明符后加一个星号*.如:
domains
lists = string*
predicates
pl(lists)1234
就说明谓词 pl 中的项 lists 是一个由串 string 组成的表.
对于由结构组成的表,至少分3步说明.例如对于下面谓 p 中的表
对于由结构组成的表,至少分3步说明.例如对于下面谓 p 中的表
p([name(“Liming”), age(20)])1
则需这样说明:
domains
rec=seg*
seg=name(string); age(integer)
predicates
p(rec)12345
2、常量与变量
由上面的领域可知, Turbo Prolog的常量有整数、实数、字符、串、符号、结构、表 和 文件 这8种数据类型.同理, Turbo Prolog 的变量也就有这8种取值.另外,变量名要求必须是以大写字母或下划线开头的字母、数字和下划线 序列,或者只有一个下划线(这种变量称为无名变量).
3、算术表达式
Turbo Prolog 提供了 5 种最基本的算术运算:加、减、乘、除 和 取模,相应运算符号为“+”、“-”、“*”、“/”、“mod”.这 5 种运算的顺序为:“*”、“/”、“mod”优先于“+”、“-”.同级从左到右按顺序运算,括号优先.
算术表达式的形式与数学中的形式基本一样.例如:
数学中的算术表达式Turbo Prolog 中的算术表达式
x + yzX + Y * Z
ab - c / dA * B - C / D
u mod vU mod V(表示求U除以V所得的余数)
即, Turbo Prolog 中算术表达式采用通常数学中使用的中缀形式.这种算术表达式为 Prolog 的一种异体结构,若以 Prolog 的结构形式来表示,则它们应为:
+(X, *(Y, Z))
-(*(A, B), /(C, D))
mod(U, V)123
所以,运算符“+”、“-”、“*”、“/”和“mod”实际也就是 Prolog 内部定义好了的函数符.
在 Turbo Prolog 程序中,如果一个算术表达式中的变元全部被实例化(即被约束),则这个算术表达式的值就会被求出.求出的值可用来实例化某变量,也可用来同其他数量进行比较,用一个算术表达式的值实例化一个变量的方法是用谓词“is”或“=”来实现的.例如:
Y is X + 5或Y = X + 5 (*)123
就使变量 Y 实例化为 X+5 的值(当然 X 也必须经已被某值实例化),可以看出,这里对变量 Y 的实例化方法类似于其他高级程序语言中的“赋值”,但又不同于赋值.例如,在 Prolog 中下面的式子是错误的:
X = X + 11
需要说明的是,虽然 Prolog 是一种逻辑程序设计语言,但在目前的硬件条件下却非突破逻辑框架不可.这是因为有些实用操作是无法用逻辑描述的(如输入与输出),有些算术运算在原则上可用逻辑描述,但这样做效率太低.为此, Prolog 提供了若干内部谓词(亦称 预定义谓词),来实现算术运算、输入与输出等操作.所谓内部谓词,就是 Prolog 的解释程序中,预先用实现语言定义好的用户可直接作为子目标调用的谓词.一般的 Prolog 实用系统都配有 100 个以上的内部谓词,这些内部谓词涉及输入输出、算术运算、搜索控制、文件操作和图形声音等方面,它们是实用 Prolog 程序设计所必不可少的.这样,上面的(*)式以及下面的关系表达式称为异体谓词.
4、关系表达式
Turbo Prolog 提供了 6 种常用的关系运算,即 小于、小于或等于、等于、大于、大于或等于、不等于,其运算符依次为:
《, 《=, =,》,》=, 《》1
Turbo Prolog 的关系表达式的形式和数学中的也基本一样,例如:
数学中的关系式Turbo Prolog 中的关系式
X + 1 ≥ YX + 1》= Y
X ≠ YX 《》 Y
即, Turbo Prolog 中的关系式也用中缀形式.当然,这种关系式为 Turbo Prolog 中的异体原子.若按 Turbo Prolog 中的原子形式来表示,则上面的两个例子为:
》=(X +1, Y) 和 《》(X, Y)1
所以上述 6 种关系运算符,实际上也就是 Turbo Prolog 内部定义好了的 6 个谓词.这 6 个关系运算符可用来比较两个算术表达式的大小.例如:
brother(Name1, Name2) :- person(Name1, man, Age1),
person(Name2, man, Age2),
mother(Z, Name1), mother(Z, Name2), Age1》 Age2.123
需要说明的是,“=”的用法比较特殊,它既可以表示比较,也可以表示约束值,即使在同一个规则中的同一个“=”也是如此.例如:
p(X, Y, Z) :- Z = X + Y.1
当变量 X、Y、Z全部被实例化时,“=”就是比较符.如对于问题:
Goal: p(3, 5, 8).1
机器回答“yes”,而对于:
Goal: p(3, 5, 7).1
机器回答“no”.即这时机器把 X+Y 的值与Z的值进行比较.但当 X,Y 被实例化,而 Z 未被实例化时, “=”号就是约束符,如:
Goal: P(3, 5, Z).1
机器回答“Z = 8”.这时,机器使 Z 实例化为 X+Y 的结果.
输入与输出
虽然 Prolog 能自动输出目标子句中的变量的值,但这种输出功能必定有限,往往不能满足实际需要;另外,对通常大多数的程序来说,运行时从键盘上输人有关数据或信息也是必不可少的.为此每种具体 Prolog 一般都提供专门的输人和输出谓词,供用户直接调用.例如,下面就是 Turbo Prolog 的几种输入输出谓词:
readin(X).这个谓词的功能是从键盘上读取一个字符串,然后约束给变量 X .
readint(X).这个谓词的功能是从键盘上读取一个整数,然后约束给变量 X,如果键盘上打入的不是整数,则该谓词失败.
readreal(X).这个谓词的功能是从键盘上读取一个实数,然后约束给变量 X,如果键盘上打入的不是实数,则该谓词失败.
readchar(X).这个谓词的功能是从键盘上读取一个字符,然后约束给变量 X,如果键盘上打入的不是单个字符,则该谓词失败.
write(X1, X2, …, Xn).这个谓词的功能是把项 Xi(i=1,2,…,n) 的值显示在屏幕上或者打印在纸上,当有某个 Xi 未实例化时,该谓词失败.其中的 Xi 可以是变量,也可以是字符串或数字.例如:
write(“computer”, “Prolog”, Y, 1992)
nl(换行谓词).它使后面的输出(如果有的话)另起一行.另外,利用 write的输出项“ ”也同样可起到换行作用.例如:
write(“name”), nl, write(“age”)
与
write(“name”, “ ”, “age”)
的效果完全一样.
举个例子:
用上面的输入输出谓词编写一个简单的学生成绩数据库查询程序.
PREDICATES
student(integer, string, real)
grade
GOAL
grade.
CLAUSES
student(1, “张三”, 90.2).
student(2, “李四”, 95.5).
student(3, “王五”, 96.4).
grade :- write(“请输人姓名:”), readln(Name),
student(_, Name, Score),
nl, write(Name, “的成绩是”, Score).
grade :- write(“对不起,找不到这个学生!”).12345678910111213
下面是程序运行时的屏幕显示
请输入姓名:王五
王五的成绩是96.412
四、分支与循环
Prolog 本来没有分支和循环语句,但可以利用其逻辑机制实现分支和循环效果.
1、分支
对于通常的 IF-THEN-ELSE 分支结构,Prolog可用两条同头的(同头即指结论相同)并列规则实现.例如,将:
IF X》0 THEN X:=1
ELSE X:=012
用 Prolog实现则是:
br :- X》 0, X = 1.
br :- X = 0.12
类似地,对于多分支,可以用多条规则实现.例如:
br :- X》 0, X = 1.
br :- X = 0, X = 0.
br :- X 《 0, X = -1.123
2、循环
Prolog 可以实现计循环次数的 FOR 循环,也可以实现不计循环次数的 DO 循环.
举个例子:
下面的程序段就实现了循环,它使得 write 语句重复执行了3次,而打印输出了3个学生的记录:
student(1, “张三”, 90.2).
student(2, “李四”, 95.5).
student(3, “王五”, 96.4).
print :- student(Number, Name, Score),
write(Number, Name, Score), nl,
Number = 3.123456
可以看出,程序第一次执行,student 谓词与第一个事实匹配,write 语句便输出了张三同学的记录.但当程序执行到最后一句时,由于 Number 不等于 3,则该语句失败,于是,引起回溯.而 write 语句和 nl 语句均只能执行一次,所以继续向上回溯到 student 语句.这样,student 谓词则因失败而重新匹配.这一次与第二个事实匹配,结果输出了李四的记录.同理,当执行到最后一句时又引起了回溯.write 语句第三次执行后,由于 Number 已等于3,所以最后一个语句成功,程序段便运行结束.
这个例子可以看做是计数循环.当然,也可以通过设置计数器而实现真正的计数循环.下面的程序段实现的则是不计数的 DO 循环:
student(1, “张三”, 90.2).
student(2, “李四”, 95.5).
student(3, “王五”, 96.4).
print :- student(Number, Name, Score),
write(Number, Name, Score), nl,
fail.
print :-.1234567
这个程序段中的 fail 是一个内部谓词,它的语义是恒失败.这个程序段与上面的程序段的差别仅在于把原来用计数器(或标记数)进行循环控制的语句变成了恒失败谓词 fail,另外又增加了一个 print 语句,增加这个语句的目的是为程序设置一个出口.因为 fail 是恒失败,下面若无出口的话,将引起 print 本身的失败,进而又会导致程序的连锁失败.
还需说明的是,用 Prolog的递归机制也可以实现循环,不过用递归实现循环通常需与表相配合.另外,递归的缺点是容易引起内存溢出,故通常的循环多是用上述方法实现的.
动态数据库
动态数据库就是在内存中实现的动态数据结构.它由事实组成,程序可以对它操作,所以在程序运行期间它可以动态变化.Turbo Prolog 提供了 3 个动态数据库操作谓词,即:
asserta(《 fact》)
assertz(《 fact》)
retract(《 fact》)123
其中 fact 表示事实.这 3 个谓词的功能如下:
asserta(《 fact》) 把 fact 插入当前动态数据库中的同名谓词的事实之前.
assertz(《 fact》) 把 fact 插入当前动态数据库中的同名谓词的事实之后.
retract(《 fact》) 把 fact 从当前动态数据库中删除.
例如语句:
asserta(student(20, “李明”, 90.5)).1
将在内存的谓词名为 student 的事实前插入一个新事实:
student(20, ‘’李明“, 90.5)1
如果内存中还没有这样的事实,则它就是第一个.又如语句:
retract(student(20, _, _)).1
将从内存的动态数据库中的删除事实:
student(20, _, _)1
它可解释为学号为 20 的一个学生的记录.注意,这里用了无名变量 “_”.
可以看出,Turbo Prolog 提供的动态数据库机制,可非常方便地实现堆栈、队列等动态数据结构,提供的数据库操作谓词大大简化了编程.
另外,Turbo Prolog 还提供了两个文件操作谓词:
save(《 filename》).
consult(《 filename》).12
其中 save 可将当前内存中的事实存入文件“filename”中,consult 则将文件“filename”中的事实调入内存.
评论
查看更多