`

Logic Programming With Prolog学习笔记(二)

阅读更多

 

第六章:循环

1、一定次数的循环,看代码,与 Erlang 一模一样:

loop(0).

loop(N):-N>0,write('The value is: '),write(N),nl,

M is N-1,loop(M).

再看一个例子:

output_values(Last,Last):- write(Last),nl,

write('end of example'),nl.

output_values(First,Last):-First=\=Last,write(First),

nl,N is First+1,output_values(N,Last).

2、循环直到条件满足:

go:-loop(start).

loop(end).

loop(X):-X\=end,write('Type end to end'),read(Word),

write('Input was '),write(Word),nl,loop(Word).

通过 ;/2谓词,可以改写为:

loop:-write('Type end to end'),read(Word),

write('Input was '),write(Word),nl,

(Word=end;loop).


3 、使用 repeat 谓词,这个谓词名称是典型的用词不当, repeat 并不重复任何东西,它仅仅是在任何时候执行的时候都是 success 。那么当回溯到 repeat 的时候,因为它是成功的,那么就要继续从 left->right 的求值目标,直到后续的某个目标满足为止,例如:

get_answer(Ans):-

write('Enter answer to question'),nl,

repeat,write('answer yes or no'),read(Ans),

valid(Ans),write('Answer is '),write(Ans),nl.

valid(yes). valid(no).

这个程序检测输入,要求玩家必须输入 yes 或者 no 才算结束,在 repeat valid(Ans) 之间,如果没有输入 yes 或者 no ,将循环多次,直到 valid(Ans) 目标被满足(也就是输入 yes 或者 no )。回溯到 repeat 的时候,总是成功,那么就继续求值后续的目标 write('answer yes or no'),read(Ans),repeat 左边的部分永远不会被回溯到。


4 fail 谓词 ,fail 谓词求值总是 fail ,因此强迫回溯开始,例如下面的例子:

dog(fido).

dog(fred).

dog(jonathan).

all_dogs:-

dog(X),write(X),write(' is a dog'),nl,fail.

all_dogs.


谓词 all_dogs 用于查询数据库中所有的 dog ,注意,最后的 all_dogs. 必须存在,不然 all_dogs. 在查找完所有的 dog 之后将总是 fail


第六章:预防回溯

1 cut 谓词:用于中止回溯,也可用!号表示。例如下面的例子:

classify(0,zero).
classify(N,negative):-N<0.
classify(N,positive).


用于检验某个数是正、负或者零。执行:

classify(-4,X).
X = negative ;
X = positive

由于不能中止回溯,当 classify(N,negative):-N<0.执行后,后续的也将执行,当然,你可以修改为:

classify(0,zero).
classify(N,negative):-N<0.
classify(N,positive):-N>0.

如果用cut谓词更好:

classify(0,zero):-!.
classify(N,negative):-N<0,!.
classify(N,positive).

尽管一些程序可以不通过cut谓词进行修改,但是有一些程序(特别是当一个谓词调用另一个谓词的时候)却是不得不借住cut谓词来中止回溯,才能实现正确的行为。

cut的另一个用途就是确定通常情况下以外的异常,与fail搭配使用,我们知道fail强迫回溯开始

例如有以下事实:

bird(sparrow).
bird(eagle).
bird(duck).
bird(crow).
bird(ostrich).
bird(puffin).
bird(swan).
bird(albatross).
bird(starling).
bird(owl).
bird(kingfisher).
bird(thrush).



假设ostrich不能fly,我们的can_fly谓词可能实现为:

can_fly(ostrich):-fail.
can_fly(X):-bird(X).

但是由于fail强制回溯,那么can_fly(ostrich).还是成功,怎么办呢?用cut:

can_fly(ostrich):-!,fail.
can_fly(X):-bird(X).

cut中止了回溯。



第8章:改变Prolog数据库

1、改变数据库:加入和删除语句

如果删除和加入语句仅仅靠consult和reconsult谓词是低效,因此Prolog提供了BIPs用于删除或者增加数据库中的语句。

如果一个谓词可以被assertz, retract等BIPs修改,那么它必须声明是动态的,否则Prolog将报错。动态声明必须放在谓词声明的前面,最好放在整个程序的前面,声明方式如下:

dynamic(mypred/3).

这就将mypred/3谓词声明为动态,可用BIPs进行增删了。

1)增加语句,通过谓词assertz/1和asserta/1,两者的区别在于:前者将语句加入相应谓词的后面,而后者将语句加入相应谓词的开始处。例如:

?-assertz(dog(fido)).
?-assertz((go:-write('hello world'),nl)).

?-assertz(dog(X)).
?-assertz((go(X):-write('hello '),write(X),nl)).



2) 删除语句,也有两个谓词:retract/1和retractall/1,两者的区别在于:前者接受一个参数,并且是一条语句,删除数据库中第一条与该语 句匹配的语句;后者仅接受语句的head部分,用于删除所有的满足该head的语句。例如,假设数据库中有如下语句:

dog(jim).
dog(fido).
dog(X).

执行

?-retract(dog(fido)).

删除数据库中的第2条语句,执行

?-retract(dog(X)).

却是删除dog(jim).因为这是第一条与(dog(X)匹配的语句,而最后的dog(X).反而得到保留。



retractall(mypred(_,_,_)).删除所有的mypred/3谓词语句。

retractall(parent(john,Y)).删除所有的第一个参数的john的parent/2语句。

retractall(mypred).删除所有的mypred/0谓词。


2、维护事实库,利用文件读写IO谓词,和本章介绍的增删谓词,就用文本文件维护事实库了,具体例子不说了。



第9章:列表处理

1、list在Prolog中是以[]包括的,以,号隔开的term组成,例如[a,b,c,d],空列表就是[]。了解过Erlang或者scheme的朋友,应该对列表很熟悉。Erlang中的列表与Prolog中的列表概念一脉相承。

2、这一章,真没啥好细谈的,列几个BIPs吧

1)member,判断元素是否在列表中

?- member(a,[a,b,c]).
yes

?- member(mypred(a,b,c),[q,r,s,mypred(a,b,c),w]).

yes

如果member的第一个参数是未绑定的变量,那么该变量将从左到右依次绑定列表中的元素。

2)length谓词,确定列表长度,第2个参数如果是变量,将变量绑定为列表参数,如果是数字,就将该数字与长度比较,相等则success,否则fail。

?- length([a,b,c,d],X).
X = 4

?- length([a,b,c],3).
yes

?- length([a,b,c],4).
no

3)reverse谓词,如果两个变量都是list,就判断是否互相倒序,如果一个是变量,一个是list,就将变量绑定为list的倒序:

?- reverse([1,2,3,4],L).
L = [4,3,2,1]
?- reverse(L,[1,2,3,4]).
L = [4,3,2,1]

?- reverse([1,2,3,4],[4,3,2,1]).
yes

4)append谓词,三个参数,如果前两个是list,第三个为变量,那么将变量绑定为两个list合并连接的列表:

?- append([1,2,3,4],[5,6,7,8,9],L).
L = [1,2,3,4,5,6,7,8,9]
?- append([],[1,2,3],L).
L = [1,2,3]


如果前两个参数包括变量,第三个是列表,那么将回溯寻找所有可能的列表组合:

?- append(L1,L2,[1,2,3,4,5]).
L1 = [] ,
L2 = [1,2,3,4,5] ;
L1 = [1] ,
L2 = [2,3,4,5] ;
L1 = [1,2] ,
L2 = [3,4,5] ;
L1 = [1,2,3] ,
L2 = [4,5] ;
L1 = [1,2,3,4] ,
L2 = [5] ;
L1 = [1,2,3,4,5] ,
L2 =[] ;

no


5) findall/3谓词比较有趣,有点类似select的概念,它有三个参数,第一个参数是一个变量或者带变量的表达式,用于确定想要find并且 collect的元素结构,第二个参数是一个goal,用于执行数据库中是否有匹配项,第三个参数是变量,用于绑定最后收集到的匹配的元素列表,例子:

假设我们已经如下事实:

person(john,smith,45,london).
person(mary,jones,28,edinburgh).
person(michael,wilson,62,bristol).
person(mark,smith,37,cardiff).
person(henry,roberts,23,london).



那么执行:

findall(S,person(_,S,_,_),L).

将返回:

L = [smith,jones,wilson,smith,roberts]

L收集了所有person的姓。如果执行:

?- findall([Forename,Surname],person(Forename,Surname,_,_),L).


将返回所有person的姓名组成的列表的列表:

L = [[john,smith],[mary,jones],[michael,wilson],[mark,smith],[henry,roberts]]


这是个非常有用的谓词。



第10章:字符串处理

1、单引号括起来的atom就是字符串,又一个Erlang沿用Prolog的典型,字符串本质上就是anscii码组成的列表,列表跟字符串可以互相转化,通过name/2谓词:

?- name('Prolog Example',L).
L = [80,114,111,108,111,103,32,69,120,97,109,112,108,101]

?-name(A,[80,114,111,108,111,103,32,69,120,97,109,112,108,101]).
A = 'Prolog Example'


2、常用谓词,一般的Prolog系统其实都有字符串扩展谓词,这里提供基本的实现:

1)连接字符串:

join2(String1,String2,Newstring):-
   name(String1,L1),name(String2,L2),
   append(L1,L2,Newlist),
   name(Newstring,Newlist).

转成列表,通过append连接成新的列表,再转成字符串。

2)Trim谓词,去除前后空格字符:

trim([A|L],L1):-A=<32,trim(L,L1).
trim([A|L],[A|L]):-A>32.

trim2(L,L1):-
reverse(L,Lrev),trim(Lrev,L2),reverse(L2,L1).

trim3(L,L1):-trim(L,L2),trim2(L2,L1).
trims(S,Snew):-name(S,L),trim3(L,L1),name(Snew,L1).

真是麻烦呐,与join2是同样的套路。

3)读入一行的readline谓词:

readline(S):-readline1([],L),name(S,L),!.
readline1(Oldlist,L):-get0(X),process(Oldlist,X,L).
process(Oldlist,13,Oldlist).
process(Oldlist,X,L):-
  append(Oldlist,[X],L1),readline1(L1,L).



第11章:高级特性


1、操作符的扩展,通过op/3谓词(略)

2、term的处理,比较有趣的=..操作符,例如:

X=..[member,A,L].

将X绑定为member(A,L).

X=..[colour,red].

X绑定为color(red),=..称为univ操作符(摘要操作符?),用于列表和term之间的相互转化,反过来:

?- data(6,green,mypred(26,blue))=..L.

L将绑定为

L = [data,6,green,mypred(26,blue)]



3、call/1谓词,接受一个call term参数,类似目标执行,例如:

call(write('Hello world')).

输出:

Hello worldyes


可执行多个term:

?-call(write('Hello world'),nl).

Hello world

yes


call跟=..联合调用:

?- X=..[write,'hello world'],call(X).
hello worldX = write('hello world')



4、functor/3谓词:当第一个参数是atom或者compound term或者某个绑定了类似值的变量,第二和第三个参数是未绑定变量,那么第二个参数变量将绑定为第一个参数的functor,第三个参数变量绑定为第一个参素的arity,举例子说明:

?- functor(write('hello world'),A,B).
A = write ,
B = 1

?- functor(start,F,A).
F = start ,
A = 0

?- functor(a+b,F,A).
F = + ,
A = 2

显然,atom的arity是0。反过来,如果第一个参数是未绑定变量,后两个参数已知:

?- functor(T,person,4).
T = person(_42952,_42954,_42956,_42958)
?- functor(T,start,0).
T = start


5、arg/3谓词,根据第一个参数数值,取第二个参数term的相应位置的参数,例如:

?- arg(3,person(mary,jones,doctor,london),X).
X = doctor

X绑定为person(mary,jones,doctor,london)的第3个参数。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics