第一章:Getting start
1、 hello world:
write(“Hello World”),nl,write(“Welcome to Prolog”),nl.
以 .号做结束,有一系列目标( goal)组成(一般也称为查询 query),目标之间用 ,号隔开,这里的 write和 nl其实是内建的 IO谓词,总共 4个目标按顺序执行, nl是开始一个新行,所有目标达成, Prolog系统会输出 yes。
2、 halt也是 BIPs,用于结束 Prolog系统, statistics用于系统统计( cpu、内存等)。 BIPs都是会产生副作用的谓词。比如 write就是会产生输出到用户屏幕副作用的 IO谓词。
3、第一个程序,写入下列代码并保存为 prog1.pl:
dog(fido).
cat(felix).
animal:-dog(X).
通过 consult谓词,将该程序导入数据库(本质上, Prolog系统就是一个全局数据库) , 分析下代码:
dog(fido).
cat(felix).
就是所谓事实( fact), dog和 cat就是谓词,而 fido和 felix是 atom,这里陈述了两个事实: fido是一只狗, felix是一只猫。 animal:-dog(X). 这句就是规则( rule), :-符号可以理解为如果 (注:if), X是变量,这里描述的规则就是:如果 X是狗,那么 X就是动物。因此,依据事实和规则, Prolog的推理系统可以推论: fido是狗,所以 fido是动物:
| ?- animal(fido).
yes
显然,根据animal规则, felix不是动物,虽然它是:
| ?- animal(felix).
no
4、 Prolog的注释 /* this is a comment */
5、查询, dog(X),查询所有满足谓词 dog的 X,也就是找出所有的狗狗。
dog(X),cat(X).查询所有是狗又是猫的“东西”。 Listing(dog),列出所有定义了谓词 dog的语句。
6、 Prolog的数据类型:
1)数字 ,Number, 623,+3,-.245
2)Atom,所有的不包括数值在内的常量,这与 Erlang 中的 atom概念是一脉相承
包括:以小写字母开头的字母、数字、下划线的组合,单引号括起来的字符序列 ,+-*/<>=@&#特殊字符组成的序列
3)变量:在查询中代表未决的 term,以大写字母或者下划线开头,跟 Erlang 一样, _表示哑元
4)组合 Term,类似其他语言中的记录结构,以一个 atom开头,后接一个参数列表,参数也都是 term,参数的个数就是它的 arity( Erlang 也是如此),例如:
read(X)
dog(henry)
likes(dog(henry),Y)等
5)列表,如:
[dog,cat,mypred,[a,b,c],hello]
6)其他类型,某些 Prolog方言可能会有字符串类型,用于处理字符串。
第二章 语句和谓词
-
一个 Prolog程序就是由一系列语句( clause)组成的,语句可以多行,以引文句号结束。两种基础语句:事实和规则。
-
事实语句,是由 atom或者组合 term构成(两者统称为 call terms),如:
chrismas.
likes(john,mary).
-
规则,形式如下:
head:-t1,t2,…,tk. (k>=1)
head成为规则的头部,也必须是 call term, :-称为颈部操作符,读作 if,而 t1,t2…tk就是语句的 body,它们描述了头部的结论所需要满足的条件。 Body是由一个或者多个 componet组成, compont就是目标,以逗号隔开,逗号表示 and的关系。从另一个角度看, head可以成为目标, body是它的子目标,规则就是为了达到目标 head,必须先达到子目标 t1,t2…tk。
例子:
large_animal(X):-animal(X),large(X).
go:-write(‘hello world’),nl.
-
谓词,所有语句的头部都包括了一个谓词定义,不同参数的同名谓词是不同的谓词,参数数目就是所谓元数( arity)。谓词可以记录为 pred/n,比如 parent/1,表示谓词名为 parent,而参数个数是一个,这个与 Erlang 相同。谓词可分为用户自定义的维持和 built-in谓词( BIPs)。 IO谓词如 write,nl就是 BIPs,它们都是总会成功的谓词,并带来相应的 IO副作用(输出和换行)。用户不能重定义 BIPs。
-
谓词的递归定义,分为直接和间接递归,例子:
likes(john,X):-likes(X,Y),dog(Y). 描述的规则为: john喜欢的是那种至少喜欢一只狗的人。
-
loading语句,包括两个 BIPs: consult/1和 reconsult/1,用于产生将文件中的语句导入数据库的副作用,两者的区别是, reconsult会覆盖之前定义的相同谓词,而 consult不会。 consult[‘prog1.pl’]可以简化为 [‘prog1.pl’].同样, reconslt[‘prog1.pl’]可以简写为 [-‘prog1.pl’]
-
变量,一开始所有的变量都是未绑定的,当目标被执行时,变量可能被绑定或者解绑定。变量存在作用域,不同语句中的同名变量没有任何关系。
-
量词,
1)全称量词,出现在事实或者规则 head部分的变量,表示事实或者规则的该变量的所有可能值,这样的变量成为全称量词,如
large_animal(X):-animal(X),large(X). 中的 X。
2)存在量词,不在事实或者规则的 head部分中存在,但是在 body部分中出现的量词,仅仅为了表示该变量至少存在一个值,这样的变量称为存在量词。例如:
person(dennis,zane,male,25,programmer).
man(A):-person(A,B,male,C,D).
man谓词 body部分的 B,C,D变量就是存在量词
9.匿名变量,就是所谓哑元,下划线 _表示,表示你并不 care这个变量的值是什么,仅仅为了占位,用以表示任意值,与 Erlang 中的意义一致。
第三章
1、这一章主要将描述 Prolog系统是如何满足目标的执行。
2、 Prolog系统是按照顺序执行目标的,每个目标都是由 call term组成,每个目标都与一个相应的谓词相关联,谓词的名称称为 functor,而参数个数就是元数( arity)。 Prolog系统是从上到下,依据语句的 head部分来匹配目标,如果匹配,将输出 yes,否则就是 no。对于用户定义的目标, Prolog没有找到任何事实或者规则与之匹配的话,那么就是 fail。这不是什么未知或者不确定,这个其实就是所谓的封闭世界假设( closed world assumption):任何结论如果无法从数据库中的事实和规则中得到证明,那么这个结论就是否定的,没有什么系统不确定或者未知这样的中间位置。
3、 Prolog的匹配算法——联合( unification),
变量可以跟任何 term联合,其中包括了变量,其实就是变量的绑定。例如:
X=1将 X与 1联合 . dog(X)与 dog(ferris),将 X与 ferris联合。
看 call terms的联合过程:
1)如果两个 call term都是 atom,只有当它们是一样的时候才可以联合,比如:
test跟 test联合成功
dennis跟' dennis'联合成功
apple跟 fuck联合失败
2)对于组合 call term的联合,要求两个 call term有一样的 functor和 arity,并且参数也要成对地联合成功。
联合的详细规则如下:
1)两个 atom当且仅当它们是一样的时候才是联合成功的。
2)两个组合的 term联合成功,当且仅当两者有一样的 functor和 airty,并且参数也要从左到右两两成对地联合成功。
3)两个 number联合成功,当且仅当它们是一样的时候,例如 7与 7联合成功,而 7与 6.9就失败
4)两个未绑定的变量总是联合成功的,两者互相绑定
5)一个未绑定变量和一个不是变量的 term也总是联合成功的,将变量绑定为该 term
6)一个绑定的变量可以被认为它所绑定的值。例如
pred(X,X,man)
与 pred(landon,dog,A) 联合失败,因为在第一个参数联合成功后, X 就被认为是 landon ,那么在尝试联合第二个参数的时候,显然 landon 与 dog 联合失败。
7)两个列表联合成功,当且仅当它们拥有相同数目的元素,并且元素间从左到右成对地联合成功。
8)嵌套结构按照这些规则递归,其他情况下的联合都是失败的。
4、目标的求值,两张图描述:
5、回溯 (backtracking),回溯就是一个返回前一个满足的目标,寻找其他方式重新满足该目标的过程。回溯,顾名思义就是退回去,再来过,当输入 ;号,就强制 Prolog系统回溯以寻找更多的匹配
6、目标求值的概览:
第四章 操作符
1、通过 op谓词,可以将用户自定义的谓词转成某种操作符,例如:
likes(dennis,catty).
我们更希望表达成:
dennis likes catty.
这在默认情况是不行的,通过 op谓词转化 likes谓词成中缀操作符:
op(150,xfy,likes).
然后就可以这样使用 likes谓词了:
dennis likes catty.
op谓词一般形式
op(操作符优先级, (xfy|xf|fy),谓词名称 )
2、算术运算:通过内建的 is/2谓词来进行算术运算, is/2是一个内建的中缀运算符,例如:
X is 2.
X is 3,Y is X+3.
出现在第二个参数的变量必须是已经绑定。 + - * /称为算术运算符,还有一类称为算术函数,它们并不是谓词,例如 abs/1 sqrt/1等,完整列表:
+ - * /
X//Y 两个整数的商
X^Y 表示 X的 Y次方,在 gnu prolog上是 **,与 ruby 一样。
-X 一元操作符
abs(X)
sqrt(X)
sin(X)
cos(X)
max(X,Y)
is/2的第一个参数也可以是表达式或者数字,在此情况下,两个参数都会被计算,如果相等,则 succeeds,否则失败。因此 is/2谓词也可以解释为,求值看两个参数是否可以联合成功。如果第一个参数是变量,就将第二个参数的值绑定在该变量上,如果第一个参数是数值或者算术表达式,就比较两个参数的值是否相等,相等则联合成功,如果第一个参数是 atom,term.list等,联合的情况依赖于实现。永远失败的 is/2:
X is X+1.总是失败。
操作符的优先级与算术中的一样,可以通过括号强制优先级。
算术比较操作符: =:=,=\=,>,>=,<,=<
3、等价操作符:
1)算术表达式比较:
E1=:=E2 成功当且仅当两个表达式求值的结果一致。
E1=\=E2 成功当且仅当两个表达式求值的结果不一致。
2) Term相同比较:
Term1==Term2成功当且仅当 Trem1跟 Term2完全相同。
因此:
likes(X,prolog)==likes(X,prolog)相同
likes(Y,prolog)==likes(Y,prolog)失败, X与 Y是不同的变量
3+7==6+4 失败,两个表达式不会被求值,显然不同
Term1 \==Term2成功当且仅当 Trem1跟 Term2不同。
3) Term联合的比较:
=操作符与 ==类似,不同在于, Term1=Term2当且仅当 Term1和 Term2联合成功的时候成功,也就是能找到某种方式绑定变量的值,来让两者相同。例如
6+X=6+3。成功, X绑定为 3
6+4=3+7 仍然失败
X=0,X=:=0. 成功,将 X绑定为 0,算术比较成功。
=的相反操作符就是 \=
4、逻辑操作符:取反操作符 not,表示 or的 ;
例如:
X=0,not X is 0. 返回 no
6<3;7 is 2+5.返回 yes