第六章:循環
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