天天看點

Logic Programming With Prolog學習筆記(二)

第六章:循環

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(n,positive):-n>0.

如果用cut謂詞更好:

classify(0,zero):-!.

classify(n,negative):-n<0,!.

盡管一些程式可以不通過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.

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(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]).

如果member的第一個參數是未綁定的變量,那麼該變量将從左到右依次綁定清單中的元素。

2)length謂詞,确定清單長度,第2個參數如果是變量,将變量綁定為清單參數,如果是數字,就将該數字與長度比較,相等則success,否則fail。

?- length([a,b,c,d],x).

x = 4

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

?- 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]).

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

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 =[] ;

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

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