該系列文章是網上的Prolog學習資料:www.learnprolognow.org的中文翻譯。希望能夠通過翻譯此學習資料,達到兩個目的:第一、系統學習prolog的知識;第二、提升英文文章理解
和翻譯能力。
內容提要:
給出一些Prolog編程的簡單例子;
Prolog的基本結構:事實,規則和查詢;
環境說明:
本系列文章使用的Prolog運行環境是:SWI-Prolog,官網地址是:http://www.swi-prolog.org。
Prolog中只有三種基礎結構:事實(facts),規則(rules)和查詢(queries)。事實和規則的集合稱為知識庫(knowledge base)(或者稱為數據庫,為了和傳統意義上的
數據庫進行區分,統一使用知識庫),Prolog的編程幾乎就是在編寫知識庫。換種說法,Prolog編程基本上等於編寫由有趣的事實和規則組合而成的知識庫。
那么如何使用Prolog程序呢?通過查詢,即通過問一些問題,這些問題的信息和答案是存儲在知識庫中的。
可能聽上去很奇怪,這和傳統意義上的編程似乎沒有什么關系,畢竟編程應該是告訴計算機做什么的吧?但是我們將會看到,Prolog的編程方式是非常有意義的,至少在特定的領域;
比如計算機語言學和人工智能領域。讓我們進入具體編寫一些簡單知識庫的實踐,在學習Prolog的過程中,實踐是最好也是唯一的方法。
Knowledge Base 1
Knowledge Base 1 (KB1)只是一些簡單事實的集合。事實是指無條件為真的一些事物、狀態或者關系。比如:我們可以定義Mia,Jody和Yolanda是女士,Jody在彈吉他,然后有
一個聚會,在Prolog中,可以通過下面5個事實進行定義:(注意每個事實的定義都是使用英文字符的句號作為結束標識符)
woman(mia).
woman(jody).
woman(yolanda).
playsAirGuitar(jody).
party.
上面的事實集合就是KB1,是我們第一個關於Prolog編程的例子。注意,上面提及的名字mia,jody,yolanda,她們的women屬性(可以先這么理解)和彈吉他,及其聚會,都是小寫字
母開頭,我們稍后會詳細解釋其中的原因。
如何使用KB1呢?通過查詢。即,通過查詢一些KB1包括的信息來使用KB1。下面是一些例子,比如我們通過下面的查詢可以問Prolog,Mia是不是一位女士:
?- women(mia).
Prolog會回答:true
因為在KB1中明確地定義了women(mia)的事實,所以Prolog的答案是true。注意,在實際使用Prolog查詢的時候,我們不需要顯式輸入?-,這個Prolog(可能不同的Prolog解釋器會略
有不同)是待輸入符號。在查詢語句的最后,一定要輸入英文句號作為結束符,如果沒有輸入,那么Prolog不會執行查詢操作,而是一直等待。
類似地,我們可以通過下面的查詢語句問Prolog,jody是否彈吉他:
?- playsAirGuitar(jody).
Prolog同樣會問答:true,因為這也是KB1中的一個事實。但是,如果我們嘗試問Mia是否彈吉他:
?- playsAirGuitar(mia).
Prolog會回答:false,因為這不是KB1中的事實,而且KB1很簡單,其他事實也不能推導出這個結論,所以Prolog認為playsAirGuitar(mia)在KB1中不能成立。
下面是兩個重要的例子。首先,如果我們這樣查詢:
?- playsAirGuitar(vincent).
Prolog又會回答false。為什么?因為這個查詢中提及的vincent這個人,在KB1中沒有與之相關的任何信息,所以Prolog認為KB1中不能推導出關於他的任何其他信息。
類似的,如果我們這樣查詢:
?- tatooed(jody).
Prolog同樣會回答false。為什么?因為這個查詢中提及的tatooed這個屬性,在KB1中沒有與之相關的任何信息,所以Prolog認為不能推導出這個屬性相關的任何其他信息。
無需多說,我們可以查詢關注的屬性,比如:
?- party.
Prolog會回答true。如果我們查詢:
?- rockConcert.
Prolog會回答false,和我們的期望是一致的。
Knowledge Base 2
下面是KB2,我們的第二個知識庫的定義:
happy(yolanda). listen2Music(mia). listen2Music(yolanda) :- happy(yolanda). playsAirGuitar(mia) :- listen2Music(mia). playsAirGuitar(yolanda) :- listen2Music(yolanda).
在KB2中,有兩個事實:listen2Music(mia)和happy(yolanda),剩下的三個都是規則。Prolog中的規則是有條件為真的一些事物、狀態或者關系。比如規則一可以這么理解:
Yolanda聽音樂如果Yolanda很高興,最后一個規則可以這么理解:Yolanda彈吉他如果Yolanda聽音樂。更抽象地理解,符號:-理解為“如果”,或者“以什么為前提”。在:-左邊的部分
是規則的頭部,在:-右邊的部分是規則的主干,所以規則可以這么理解:如果一個規則的主干為真,那么這個規則的頭部也為真。所以,以下是Prolog規則運用的要點:
如果知識庫包括了一個規則,head :- body,並且Prolog知道在知識庫中,body部分為真,那么Prolog就能夠推導head為真。推導的基礎步驟稱為假言推理(modus ponens)。
讓我們繼續看具體的例子,如果我們查詢Mia是否彈吉他:
?- playsAirGuitar(mia).
Prolog會回答true,為什么?畢竟在KB2中,playsAirGuitar(mia)不是一個事實,但是可以找到關於它的一個規則:
playsAirGuitar(mia) :- listen2Music(mia).
可以明確知道,KB2中包含了listen2Music(mia)的事實。所以Prolog可以使用規則的假言推理推導出playsAirGuitar(mia)為真。
下面另外一個例子顯示,Prolog可以使用假言推理鏈,如果我們查詢:
?- playsAirGuitar(yolanda).
Prolog會回答true,為什么?首先,通過使用happy(yolanda)的事實,及其相關的規則:
listen2Music(yolanda) :- happy(yolanda).
Prolog可以推導出一個新的事實:listen2Music(yolanda)。這個新事實在知識庫中是隱式存在的(通過推導得出),但是,Prolog可以像使用顯式的事實一樣使用它。接下來,
通過這個推導的事實及其規則:
playsAirGuitar(yolanda) :- listen2Music(yolanda).
Prolog可以推導中新的事實:playsAirGuitar(yolanda),即我們的查詢結果為真。總結一下:一個假言推理的任何事實,可以用作其他規則的輸入,通過鏈接的方式,將所有的假
言推理應用組合起來,Prolog就可以從知識庫包含的事實和規則中推導出任何符合邏輯的信息。
在知識庫中的事實和規則統稱為子句(clauses)。所以KB2包括了5個子句,其中有3個規則和2個事實。另外一種看待KB2的方式可以這么說,它是由3個謂詞(predicates)(或者
稱為procedures)組成,三個謂詞是:listen2Music,happy,playsAirtGuitar。(謂詞和之前出現的屬性是相同的含義,為了統一,今后統一使用謂詞)
其中happy謂詞由一個獨立的子句(一個事實)組成。listen2Music和playsAirGuitar謂詞分別由兩個子句(listen2Music兩個子句一個是事實,另外一個是規則;playsAirGuitar
兩個子句都是規則)組成。可以認為Prolog編程就是由謂詞構成的。本質上來說,謂詞的概念很重要,編程中各種子句都是關於謂詞代表的含義及其推導的含義。
還有一點可以提及的是,我們可以把事實看着沒有主干的規則,即我們可以認為事實是無論任何條件都成立的規則。
Knowledge Base 3
KB3,我們的第三個知識庫,由5個子句組成:
happy(vincent). listen2Music(butch). playsAirGuitar(vincent) :- listen2Music(vincent), happy(vincent). playsAirGuitar(butch) :- happy(butch). playsAirGuitar(butch) :- listen2Music(butch).
KB3中定義了兩個事實,happy(vincent)和listen2Music(butch),及其三個規則。KB3中的定義了三個名字和KB2中一樣的謂詞(happy,listen2Music,和playsAirGuitar),但是
其實現不同,特別是在playsAirGuitar的謂詞中引入了一些新的含義。首先,分析這個規則:
playsAirGuitar(vincent) :-
listen2Music(vincent),
happy(vincent).
其中主干部分有兩項,或者說兩個目標組成。這里最重要的是英文逗號字符,它分隔了目標listens2Music(vincent)和目標happy(vincent)。這是邏輯與在Prolog中的表現形式。所以,
可以這么理解:“Vincent彈吉他如果他聽音樂並且他很快樂”。
所以,如果我們查詢:
?- playsAirGuitar(vincent).
Prolog會回答false。這是因為,KB3包含happy(vincent)的事實,但是沒有明確地包含listen2Music(vincent),並且也不能被推導出來。所以KB3只能滿足playsAirGuitar(vincent)
兩個條件之一,所以查詢失敗,Prolog回答false。
順便提及一下,空格在Prolog中是沒有意義的,比如,我們也可以這么書寫:
playsAirGuitar(vincent) :-
happy(vincent),
listen2Music(vincent).
這個和之前的定義是同樣效果。Prolog提供了很高的書寫自由度,便於我們書寫出可讀性高的程序代碼。
接下來,分析KB3中有相同頭部的兩個規則:
playsAirGuitar(butch) :- happy(butch).
playsAirGuitar(butch) :- listen2Music(butch).
這里表達的意思是,Butch彈吉他如果他聽音樂,或者他很高興。即,多個有相同頭部的規則是Prolog中邏輯或的表達方式。所以,如果我們查詢:
?- playsAirGuitar(butch).
Prolog會回答true。雖然第一個規則對於這個查詢沒有作用(因為happy(butch)在KB3中不存在,也不能被推導),但是KB3包含了listen2Music(butch),所以Prolog可以使用
假言推理:
playsAirGuitar(butch) :- listen2Music(butch).
推導出playsAirGuitar(butch)為真。
Prolog中存在另外一種方式表示邏輯或,可以使用如下的定義來替代之前的兩個規則:
playsAirGuitar(butch) :-
happy(butch);
listen2Music(butch).
英文分號字符在Prolog中也表示邏輯或,所以這個單一規則的含義,和之前兩個規則的含義是相同的。那么使用多個規則,還是使用英文分號,哪個更好呢?這個需要根據情況來判斷。
一方面,使用分號會讓Prolog代碼的可讀性變差,但是另一方面,使用分號后規則數量變少,使得處理效率更好。
在上面的學習中,可以看出Prolog中明確包括了很多邏輯標識,比如,:-表示“如果”;英文字符逗號“,”表示邏輯與;英文字符分號“;”表示邏輯或。而且,我們可以看到標准的
邏輯證明規則(假言推理)在Prolog中起到了重要作用。所以,我們可以開始理解,為什么“Prolog”這個名字是“Programming with logic”的簡寫了,:)。
Knowledge Base 4
下面是KB4,我們的第四個知識庫的定義:
woman(mia).
woman(jody).
woman(yolanda).
loves(vincent, mia).
loves(marsellus, mia).
loves(pumpkin, honey_bunny).
loves(honey_bunny, pumpkin).
哈哈,這是一個相當無聊的知識庫。這里沒有規則,只有事實的集合。當然,我們可以第一次接觸一個謂詞中存在兩個名字(這里指loves定義的關系)。
但是事實並非如此,這個例子的新意不在於知識庫的定義,而是我們查詢的方式。事實上,這是我們第一次接觸Prolog的變量使用,如下:
?- woman(X).
X即代表一個變量(在Prolog中,大寫字母開頭的單詞代表變量,這也是為什么我們之前的例子中,所有出現的字符都是小寫字母開頭的原因)。這里X不是一個具體的名字,
它更像一個信息的占位符號。即,這個查詢就是問Prolog:告訴我們你知道都有哪些人是女士(woman)?
Prolog通過在KB4中從上至下遍歷來回答這個查詢,Prolog會試圖找到(或者匹配)表達式woman(X)的信息。在KB4中,第一個事實是woman(mia),所以Prolog會將X和mia合一,
這樣可以完美地符合查詢(順便提及一下,這個處理流程中Prolog做了很多的操作:我們可以簡單地理解為,Prolog將X初始化為mia,或者將X值綁定成mia)。Prolog會將結果返回,
如下:
X = mia
這里不僅僅說至少有一個滿足查詢的結果存在於KB4中,而且明確地告訴了這個結果值。這里Prolog不是回答true,而是實際給出能夠滿足查詢的變量綁定(變量初始化)。
但是,這不是結束。變量的要點是它們能夠代表,或者說能夠合一不同的符合查詢的信息。而且有其他的women在知識庫中,也滿足這個查詢。所以,我們可以輸入英文字符”;“
繼續查詢下一個匹配的結果:
X = mia;
因為英文字符”;“代表是邏輯或,所以這個查詢可以理解為:還有其他結果嗎?所以Prolog有會繼續遍歷知識庫(它會標記上一次結果的地點,並且從那里繼續),尋找下一個可能
的結果,並且找到jody也滿足,所以Prolog會回答:
X = mia;
X = jody
這里就告訴了我們基於KB4第二個符合查詢的結果值。當然,如果我們繼續輸入英文字符”;“,Prolog會繼續回答:
X = mia;
X = jody;
X = yolanda
但是當我們第三次輸入英文字符”;“會發生什么?Prolog會回答false,沒有其他合一的可能了。因為在KB4中沒有women開頭的事實了,剩余的4個規則都是關於loves關系的,
所以沒有辦法再對woman(X)進行合一。
接下來,我們嘗試一個更為復雜的查詢,如下:
?- loves(marsellus, X), woman(X).
前面已經介紹過,英文字符逗號“,”表示邏輯與,所以這個查詢的含義是:是否有這樣的X,它既能夠滿足Marsellus愛它,並且還是一名女士?如果你再看知識庫,會發現:
Mia是一名女人(事實一),同時Marsellus愛Mia(事實5)。Prolog能夠模擬這個能力,把結果找出來。即Prolog能夠遍歷整個知識庫,並將X和Mia合一,使得我們查詢中的兩個
子查詢都滿足。最后,Prolog會回答:
X = mia
能夠將變量和知識庫中的信息進行合一是Prolog的核心。隨着我們的深入學習,我們會發現很多Prolog的有趣思想——但是Prolog能夠進行合一並返回變量綁定信息的能力是所有
這一切的關鍵點。
Knowledge Base 5
好的,我們之前介紹了變量,但是僅僅是在查詢中使用到。其實變量也可以應用在知識庫中。而且只有這樣做,我們才能寫出真正有用的Prolog程序。下面是一個簡單的例子,
知識庫KB5的定義:
loves(vincent, mia).
loves(marsellus, mia).
loves(pumpkin, honey_bunny).
loves(honey_bunny, pumpkin).
jealous(X, Y) :- loves(X, Z), loves(Y, Z).
KB5包含了4個loves關系的事實和一個規則,這個規則是我們至今定義的最有趣的一個:它包括了三個變量(X,Y,Z),這個規則如何理解?
本質上來說,這個規則定義“情敵”的概念。可以這里理解,如果X愛Z,同時Y也愛Z,那么X和Y就是情敵(呵呵,這里的情敵僅僅限於學習,現實情況會復雜的多,:))。
關鍵的一點在於這是一個通用的陳述,其中不涉及具體的人,比如mia,pumpkin等——從某種程度來說,是指世界上的每個人。這就是抽象。
如果我們進行查詢:
?- jealous(marsellus, W).
這個查詢的含義是:是否存在這樣的一個人W,他和Marsellus是“情敵”?Vincent就是這個人。如果你檢查“情敵”的定義,你可以發現Marsellus和Vincent就是“情敵”,
因為他們兩個人都愛同樣的一個女士,即Mia。所以Prolog會回答:
W = vincent