Logic Programming With Prolog學(xué)習(xí)筆記(二)
Posted on 2008-05-28 23:07 dennis 閱讀(4086) 評(píng)論(0) 編輯 收藏 所屬分類: 動(dòng)態(tài)語(yǔ)言第六章:循環(huán)
1、一定次數(shù)的循環(huán),看代碼,與Erlang一模一樣:
loop(0).
loop(N):-N>0,write('The value is: '),write(N),nl,
M is N-1,loop(M).
再看一個(gè)例子:
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、循環(huán)直到條件滿足:
go:-loop(start).
loop(end).
loop(X):-X\=end,write('Type end to end'),read(Word),
write('Input was '),write(Word),nl,loop(Word).
通過(guò);/2謂詞,可以改寫(xiě)為:
loop:-write('Type end to end'),read(Word),
write('Input was '),write(Word),nl,
(Word=end;loop).
3、使用repeat謂詞,這個(gè)謂詞名稱是典型的用詞不當(dāng),repeat并不重復(fù)任何東西,它僅僅是在任何時(shí)候執(zhí)行的時(shí)候都是success。那么當(dāng)回溯到repeat的時(shí)候,因?yàn)樗浅晒Φ模敲淳鸵^續(xù)從left->right的求值目標(biāo),直到后續(xù)的某個(gè)目標(biāo)滿足為止,例如:
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).
這個(gè)程序檢測(cè)輸入,要求玩家必須輸入yes或者no才算結(jié)束,在repeat到valid(Ans)之間,如果沒(méi)有輸入yes或者no,將循環(huán)多次,直到valid(Ans)目標(biāo)被滿足(也就是輸入yes或者no)。回溯到repeat的時(shí)候,總是成功,那么就繼續(xù)求值后續(xù)的目標(biāo)write('answer yes or no'),read(Ans),repeat左邊的部分永遠(yuǎn)不會(huì)被回溯到。
4、fail謂詞,fail謂詞求值總是fail,因此強(qiáng)迫回溯開(kāi)始,例如下面的例子:
dog(fido).
dog(fred).
dog(jonathan).
all_dogs:-
dog(X),write(X),write(' is a dog'),nl,fail.
all_dogs.
謂詞all_dogs用于查詢數(shù)據(jù)庫(kù)中所有的dog,注意,最后的all_dogs.必須存在,不然all_dogs.在查找完所有的dog之后將總是fail。
第六章:預(yù)防回溯
1、cut謂詞:用于中止回溯,也可用!號(hào)表示。例如下面的例子:
classify(0,zero).
classify(N,negative):-N<0.
classify(N,positive).
用于檢驗(yàn)?zāi)硞€(gè)數(shù)是正、負(fù)或者零。執(zhí)行:
classify(-4,X).
X = negative ;
X = positive
由于不能中止回溯,當(dāng)classify(N,negative):-N<0.執(zhí)行后,后續(xù)的也將執(zhí)行,當(dāng)然,你可以修改為:
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).
盡管一些程序可以不通過(guò)cut謂詞進(jìn)行修改,但是有一些程序(特別是當(dāng)一個(gè)謂詞調(diào)用另一個(gè)謂詞的時(shí)候)卻是不得不借住cut謂詞來(lái)中止回溯,才能實(shí)現(xiàn)正確的行為。
cut的另一個(gè)用途就是確定通常情況下以外的異常,與fail搭配使用,我們知道fail強(qiáng)迫回溯開(kāi)始
例如有以下事實(shí):
bird(sparrow).
bird(eagle).
bird(duck).
bird(crow).
bird(ostrich).
bird(puffin).
bird(swan).
bird(albatross).
bird(starling).
bird(owl).
bird(kingfisher).
bird(thrush).
假設(shè)ostrich不能fly,我們的can_fly謂詞可能實(shí)現(xiàn)為:
can_fly(ostrich):-fail.
can_fly(X):-bird(X).
但是由于fail強(qiáng)制回溯,那么can_fly(ostrich).還是成功,怎么辦呢?用cut:
can_fly(ostrich):-!,fail.
can_fly(X):-bird(X).
cut中止了回溯。
第8章:改變Prolog數(shù)據(jù)庫(kù)
1、改變數(shù)據(jù)庫(kù):加入和刪除語(yǔ)句
如果刪除和加入語(yǔ)句僅僅靠consult和reconsult謂詞是低效,因此Prolog提供了BIPs用于刪除或者增加數(shù)據(jù)庫(kù)中的語(yǔ)句。
如果一個(gè)謂詞可以被assertz, retract等BIPs修改,那么它必須聲明是動(dòng)態(tài)的,否則Prolog將報(bào)錯(cuò)。動(dòng)態(tài)聲明必須放在謂詞聲明的前面,最好放在整個(gè)程序的前面,聲明方式如下:
dynamic(mypred/3).
這就將mypred/3謂詞聲明為動(dòng)態(tài),可用BIPs進(jìn)行增刪了。
1)增加語(yǔ)句,通過(guò)謂詞assertz/1和asserta/1,兩者的區(qū)別在于:前者將語(yǔ)句加入相應(yīng)謂詞的后面,而后者將語(yǔ)句加入相應(yīng)謂詞的開(kāi)始處。例如:
?-assertz(dog(fido)).
?-assertz((go:-write('hello world'),nl)).
?-assertz(dog(X)).
?-assertz((go(X):-write('hello '),write(X),nl)).
2) 刪除語(yǔ)句,也有兩個(gè)謂詞:retract/1和retractall/1,兩者的區(qū)別在于:前者接受一個(gè)參數(shù),并且是一條語(yǔ)句,刪除數(shù)據(jù)庫(kù)中第一條與該語(yǔ) 句匹配的語(yǔ)句;后者僅接受語(yǔ)句的head部分,用于刪除所有的滿足該head的語(yǔ)句。例如,假設(shè)數(shù)據(jù)庫(kù)中有如下語(yǔ)句:
dog(jim).
dog(fido).
dog(X).
執(zhí)行
?-retract(dog(fido)).
刪除數(shù)據(jù)庫(kù)中的第2條語(yǔ)句,執(zhí)行
?-retract(dog(X)).
卻是刪除dog(jim).因?yàn)檫@是第一條與(dog(X)匹配的語(yǔ)句,而最后的dog(X).反而得到保留。
retractall(mypred(_,_,_)).刪除所有的mypred/3謂詞語(yǔ)句。
retractall(parent(john,Y)).刪除所有的第一個(gè)參數(shù)的john的parent/2語(yǔ)句。
retractall(mypred).刪除所有的mypred/0謂詞。
2、維護(hù)事實(shí)庫(kù),利用文件讀寫(xiě)IO謂詞,和本章介紹的增刪謂詞,就用文本文件維護(hù)事實(shí)庫(kù)了,具體例子不說(shuō)了。
第9章:列表處理
1、list在Prolog中是以[]包括的,以,號(hào)隔開(kāi)的term組成,例如[a,b,c,d],空列表就是[]。了解過(guò)Erlang或者scheme的朋友,應(yīng)該對(duì)列表很熟悉。Erlang中的列表與Prolog中的列表概念一脈相承。
2、這一章,真沒(méi)啥好細(xì)談的,列幾個(gè)BIPs吧
1)member,判斷元素是否在列表中
?- member(a,[a,b,c]).
yes
yes
如果member的第一個(gè)參數(shù)是未綁定的變量,那么該變量將從左到右依次綁定列表中的元素。
2)length謂詞,確定列表長(zhǎng)度,第2個(gè)參數(shù)如果是變量,將變量綁定為列表參數(shù),如果是數(shù)字,就將該數(shù)字與長(zhǎng)度比較,相等則success,否則fail。
?- length([a,b,c,d],X).
X = 4
?- length([a,b,c],3).
yes
?- length([a,b,c],4).
no
3)reverse謂詞,如果兩個(gè)變量都是list,就判斷是否互相倒序,如果一個(gè)是變量,一個(gè)是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謂詞,三個(gè)參數(shù),如果前兩個(gè)是list,第三個(gè)為變量,那么將變量綁定為兩個(gè)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]
如果前兩個(gè)參數(shù)包括變量,第三個(gè)是列表,那么將回溯尋找所有可能的列表組合:
?- 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謂詞比較有趣,有點(diǎn)類似select的概念,它有三個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)變量或者帶變量的表達(dá)式,用于確定想要find并且 collect的元素結(jié)構(gòu),第二個(gè)參數(shù)是一個(gè)goal,用于執(zhí)行數(shù)據(jù)庫(kù)中是否有匹配項(xiàng),第三個(gè)參數(shù)是變量,用于綁定最后收集到的匹配的元素列表,例子:
假設(shè)我們已經(jīng)如下事實(shí):
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).
那么執(zhí)行:
findall(S,person(_,S,_,_),L).
將返回:
L = [smith,jones,wilson,smith,roberts]
L收集了所有person的姓。如果執(zhí)行:
?- findall([Forename,Surname],person(Forename,Surname,_,_),L).
將返回所有person的姓名組成的列表的列表:
L = [[john,smith],[mary,jones],[michael,wilson],[mark,smith],[henry,roberts]]
這是個(gè)非常有用的謂詞。
第10章:字符串處理
1、單引號(hào)括起來(lái)的atom就是字符串,又一個(gè)Erlang沿用Prolog的典型,字符串本質(zhì)上就是anscii碼組成的列表,列表跟字符串可以互相轉(zhuǎn)化,通過(guò)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系統(tǒng)其實(shí)都有字符串?dāng)U展謂詞,這里提供基本的實(shí)現(xiàn):
1)連接字符串:
join2(String1,String2,Newstring):-
name(String1,L1),name(String2,L2),
append(L1,L2,Newlist),
name(Newstring,Newlist).
轉(zhuǎn)成列表,通過(guò)append連接成新的列表,再轉(zhuǎn)成字符串。
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章:高級(jí)特性
1、操作符的擴(kuò)展,通過(guò)op/3謂詞(略)
2、term的處理,比較有趣的=..操作符,例如:
X=..[member,A,L].
將X綁定為member(A,L).
X=..[colour,red].
X綁定為color(red),=..稱為univ操作符(摘要操作符?),用于列表和term之間的相互轉(zhuǎn)化,反過(guò)來(lái):
?- data(6,green,mypred(26,blue))=..L.
L將綁定為
L = [data,6,green,mypred(26,blue)]
3、call/1謂詞,接受一個(gè)call term參數(shù),類似目標(biāo)執(zhí)行,例如:
call(write('Hello world')).
輸出:
Hello worldyes
可執(zhí)行多個(gè)term:
?-call(write('Hello world'),nl).
Hello world
yes
call跟=..聯(lián)合調(diào)用:
?- X=..[write,'hello world'],call(X).
hello worldX = write('hello world')
4、functor/3謂詞:當(dāng)?shù)谝粋€(gè)參數(shù)是atom或者compound term或者某個(gè)綁定了類似值的變量,第二和第三個(gè)參數(shù)是未綁定變量,那么第二個(gè)參數(shù)變量將綁定為第一個(gè)參數(shù)的functor,第三個(gè)參數(shù)變量綁定為第一個(gè)參素的arity,舉例子說(shuō)明:
?- 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。反過(guò)來(lái),如果第一個(gè)參數(shù)是未綁定變量,后兩個(gè)參數(shù)已知:
?- functor(T,person,4).
T = person(_42952,_42954,_42956,_42958)
?- functor(T,start,0).
T = start
5、arg/3謂詞,根據(jù)第一個(gè)參數(shù)數(shù)值,取第二個(gè)參數(shù)term的相應(yīng)位置的參數(shù),例如:
?- arg(3,person(mary,jones,doctor,london),X).
X = doctor
X綁定為person(mary,jones,doctor,london)的第3個(gè)參數(shù)。