莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          Logic Programming With Prolog學習筆記(二)

          Posted on 2008-05-28 23:07 dennis 閱讀(4086) 評論(0)  編輯  收藏 所屬分類: 動態語言

          第六章:循環

          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才算結束,在repeatvalid(Ans)之間,如果沒有輸入yes或者no,將循環多次,直到valid(Ans)目標被滿足(也就是輸入yes或者no)。回溯到repeat的時候,總是成功,那么就繼續求值后續的目標write('answer yes or no'),read(Ans),repeat左邊的部分永遠不會被回溯到。


          4fail謂詞,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


          第六章:預防回溯

          1cut謂詞:用于中止回溯,也可用!號表示。例如下面的例子:

          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個參數。

          主站蜘蛛池模板: 凤台县| 蓬安县| 观塘区| 兴化市| 县级市| 静安区| 蕲春县| 湾仔区| 岳池县| 无极县| 封丘县| 丰城市| 监利县| 武鸣县| 永靖县| 略阳县| 平罗县| 灵寿县| 福贡县| 汶川县| 长沙县| 沈丘县| 潜山县| 运城市| 千阳县| 连云港市| 家居| 通山县| 手游| 眉山市| 西乌珠穆沁旗| 襄垣县| 沅陵县| 紫阳县| 石景山区| 凤阳县| 吉安县| 康马县| 和田市| 哈巴河县| 万全县|