Logic Programming With Prolog学习笔记(二)

    xiaoxiao2024-02-19  154

    第六章:循环

    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个参数。

    文章转自庄周梦蝶  ,原文发布时间 2008-05-28

    相关资源:NLW3-源码
    最新回复(0)