跳至內容

Prolog/引言

來自 Wikibooks,開放世界中的開放書籍

本節介紹如何安裝 Prolog 編譯器、載入第一個程式並對其進行查詢。然後解釋如何在程式和查詢中使用事實和變數。

在開始任何操作之前,需要在您的系統上安裝 Prolog 編譯器和文字編輯器。文字編輯器允許您編寫 Prolog 程式,而 Prolog 編譯器(也稱為直譯器)則允許您執行它們。

Prolog 編譯器

以下 Prolog 實現是免費的(至少用於個人或教育用途,請務必閱讀條款)。只需下載一個並根據網站上的說明進行安裝。

  • B-Prolog
http://www.picat-lang.org/bprolog/
一個標準 Prolog 編譯器,具有諸如表格和約束求解等擴充套件功能。支援多種平臺。
  • SWI Prolog(推薦用於本書)
https://www.swi-prolog.org/
一個小型且健壯的開源實現,符合 Prolog 標準(ISO 和 Edinburgh),並具有許多額外的庫和內建謂詞。甚至還有一個用於建立視窗和圖形的單獨工具包,稱為 XPCE。支援多種平臺。
  • GNU Prolog
http://www.gprolog.org/(曾為:http://pauillac.inria.fr/~diaz/gnu-prolog/
一個相對較新的開源實現。支援約束邏輯程式設計,這是 Prolog 的一種擴充套件。
  • Visual Prolog
http://www.visual-prolog.com/
一個完整的 Prolog 面向物件擴充套件開發環境。包括編譯器、連結器、文字編輯器、圖形對話方塊編輯器、構建系統、偵錯程式、大型庫等等。

以下實現不是免費的

  • SICSTUS Prolog
http://www.sics.se/
可能是最著名的專業 Prolog 實現和開發套件。符合 ISO 標準,許多庫和對約束邏輯程式設計的支援。免費評估。
  • Quintus Prolog
http://www.sics.se/quintus/
一個特別健壯可靠的實現,旨在用於商業用途和研究專案,由與 SICSTUS 同一家公司開發。免費評估。

文字編輯器

您將編寫的程式是簡單的文字檔案,可以使用任何文字編輯器進行讀取和寫入。一些 Prolog 實現帶有自己的編輯器,但對於那些沒有的實現,這裡列出了一些文字編輯器。這些提供了編寫 Prolog 程式所需的基本功能,例如縮排、括號匹配,有些甚至可以調整以突出顯示 Prolog 程式碼的語法。

  • Crimson Editor
http://www.crimsoneditor.com/
一個免費的 Windows 文字編輯器,具有許多功能。
  • GNU Emacs
http://www.gnu.org/software/emacs/emacs.html
Unix 經典文字編輯器的免費開源實現。可能難以理解,但功能豐富。
  • Vim
http://www.vim.org/
Emacs 長期競爭對手的免費開源實現。
  • Textpad
http://www.textpad.com/
一個具有許多功能的 Windows 文字編輯器。免費評估。
  • Eclipse Prolog 外掛
http://eclipse.ime.usp.br/projetos/grad/plugin-prolog/index.html
一個免費的 Eclipse 外掛。

第一步

[編輯 | 編輯原始碼]

安裝完 Prolog 實現後,就可以編寫第一個程式並將其載入到直譯器中(主要是為了檢查一切是否正常)。啟動您的文字編輯器,並建立一個文字檔案,其中只包含以下一行

human(john).

請務必準確,Prolog 中大小寫敏感,句點也很重要。這將是您的程式(也稱為**資料庫**或**知識庫**)。為其指定一個好聽的名字,例如 prolog1.pl 並儲存。注意:副檔名 pl 未正式與 Prolog 關聯,如果也使用 Perl 程式設計,它也使用 .pl,可能會導致衝突。如果出現此問題,您可以使用 pro 或 pr 或任何您喜歡的名稱。現在啟動您的 Prolog 直譯器。大多數 Prolog 直譯器會顯示一個包含一些啟動資訊的視窗,然後顯示一行

?- 

並在其後顯示一個游標。通常有一個選單用於將檔案載入到直譯器中。如果沒有,您可以鍵入以下內容載入您的檔案

consult('FILEPATH').

然後按 Enter。再次強調,請務必準確,不要使用大寫字母,並記住句點。將 FILEPATH 替換為您檔案的名稱和目錄。例如,如果您的檔案位於 C:\My Documents\Prolog\prolog1.pl,則使用

consult('c:/my documents/prolog/prolog1.pl').

或簡寫

['c:/my documents/prolog/prolog1.pl'].

請注意,斜槓的方向相反,因為反斜槓 (\) 在 Prolog(和大多數其他語言)中具有特殊含義。如果您使用的是基於 UNIX 的系統(例如 Linux),則命令可能如下所示

consult('/home/yourName/prolog/prolog1.pl').
['/home/yourName/prolog/prolog1.pl'].

您的直譯器現在應該會告訴您檔案已成功載入。如果未成功載入,請查閱您的實現的幫助檔案或手冊以瞭解如何載入檔案。

此外,您可以告訴 Prolog 直譯器在使用 -s 鍵執行時自動載入檔案,如下所示[1]

prolog -s /home/yourName/prolog/prolog1.pl

顯示一些資訊後,您將看到

?-

要檢視一切是否正常,請鍵入

human(john).

(不要忘記句點)然後按 Enter。Prolog 將回答

true.

鍵入

human(Who).

,按 Enter,Prolog 將回答

Who = john.

要退出 Prolog,請鍵入

halt.

語法、事實和查詢

[編輯 | 編輯原始碼]

上一示例中的 human(john). 行是一個謂詞形式的 Prolog 語句。此類語句稱為事實。謂詞由一個或多個字元組成的一個單片語成,全部小寫,後面可能跟著若干個項。以下是有效謂詞的示例

human(john)
father(david, john)
abc(def,ghi,jkl,m)
tree
p(a ,f ,d)

項(括號內的“單詞”)可以採用多種形式,但現在我們將堅持使用常量。這些是單詞,同樣全部小寫。謂詞和常量的第一個字元都需要是字母。使用謂詞,我們可以向程式新增事實

human(john).
human(suzie).
human(eliza).
man(david).
man(john).
woman(suzie).
woman(eliza).
parent(david, john).
parent(john, eliza).
parent(suzie, eliza).

請注意每行後面的句點“.”,表示該行結束。這一點非常重要,如果忘記了,您的直譯器將無法理解程式。您還應該注意,為謂詞和項選擇的名稱實際上對 Prolog 直譯器沒有任何意義。它們只是為了顯示程式的含義。我們可以輕鬆地將 human 替換為 spaceship,直譯器將無法分辨。

如果我們將上述程式載入到直譯器中,我們就可以對其執行一個查詢。如果您鍵入

human(john).

Prolog 將回答

true.

如果您鍵入

woman(john).

Prolog 將回答

false.

這看起來也很明顯,但重要的是要以正確的方式理解它。如果您向 Prolog 提問 human(john).,則表示您正在詢問 Prolog 此語句是否為真。顯然,Prolog 無法從語句中看出它是否為真,因此它會查閱您的檔案。它會檢查程式中的所有行,以檢視是否有任何行與該語句匹配,如果找到匹配項,則回答 Yes。如果沒有找到匹配項,則回答 false.。請注意,如果您詢問

 ?- human(david).

Prolog 將回答 false.,因為我們尚未將該事實新增到資料庫中。這一點很重要:如果 Prolog 無法從程式中證明某件事,它將認為它不正確。這被稱為封閉世界假設

我們將使用 human(david) 更新程式,以便資料庫中的所有人都是人類,並且是男性或女性

 human(david).
 human(john).
 human(suzie).
 human(eliza).
 man(david).
 man(john).
 woman(suzie).
 woman(eliza).
 parent(david, john).
 parent(john, eliza).
 parent(suzie, eliza).

我們現在擁有的仍然不是一種表達能力很強的語言。透過在查詢中使用變數,我們可以獲得更大的表達能力。變數是一個單詞,就像項和謂詞一樣,但它以大寫字母開頭,之後可以包含大小寫字元。考慮以下查詢

human(A).

現在,謂詞的項是一個變數。Prolog 將嘗試將項繫結到變數。換句話說,您正在詢問 Prolog,A 需要是什麼才能使 human(A) 為真。

?- human(A).

Prolog 將回答

A = david ;

這是真的,因為資料庫包含行 human(david)。如果您按 Enter,Prolog 將回答 Yes 並將游標返回給您。如果您按分號 ;,Prolog 將顯示其餘的可能性

A = john ;
A = suzie ;
A = eliza.

在eliza之後,沒有其他的可能性了。如果你用多個變數查詢Prolog,它會顯示所有使查詢為真的變數例項化。

 ?- parent(Parent, Child).

 Parent = david
 Child = john ;

 Parent = john
 Child = eliza ;

 Parent = suzie
 Child = eliza ;

 No

當Prolog被要求用一個變數進行查詢時,它會檢查程式的所有行,並嘗試將每個謂詞統一到查詢中。這意味著它會檢查當變數以某種方式例項化時,查詢是否與謂詞匹配。它可以透過使A為john來將human(A)human(john)統一,但它不能將man(A)human(john)統一,因為謂詞不匹配。

如果我們想讓Prolog更難處理,我們可以在查詢中使用兩個謂詞,例如

 ?- human(A), parent(B,A).

現在我們要求Prolog找出一個名叫A的人,其父母為B。逗號表示並且,表示兩個謂詞都需要為真,查詢才能為真。為了檢查這個查詢,Prolog首先會找到一個例項化來使第一個謂詞為真——假設它使A等於john——然後它會嘗試使第二個謂詞為真——A等於john。如果它找到了兩個使這兩個謂詞都為真的A和B的例項化,它會將它們返回給你。你可以按Enter鍵結束程式,或按分號檢視更多選項。

Prolog可能會為A選擇一個滿足第一個謂詞但與第二個謂詞不匹配的選項。假設它選擇A = suzie來滿足human(A);沒有B的選擇可以滿足parent(B, suzie),所以Prolog會放棄對A的選擇suzie,並嘗試另一個名字。這稱為回溯

在上面的例子中,Prolog首先會在程式中找到human(david)並將A與david統一。為了使第二個謂詞為真,它需要找到parent(B, david)的例項化。它找不到任何例項化,所以它會尋找human(A)的新例項化。它嘗試下一個選項:A = john。現在它需要例項化parent(B, john)。它在行parent(david, john)中找到B = david,並將其報告給你。

A = john
B = david 

如果你按下分號,它會嘗試找到第二個謂詞的新例項化。如果失敗,它會嘗試找到第一個謂詞的新例項化,依此類推,直到它用完所有選項。

有一個特殊的變數,稱為匿名變數,使用下劃線(_)字元。當你在查詢中使用這個字元時,你基本上是在說你不在乎這個變數是如何例項化的,也就是說,你不在乎它繫結到哪個項,只要它繫結到某個東西即可。如果你詢問Prolog

 ?- parent(A, _). 

Prolog 將回答

A = david;
A = john;
A = suzie;

它不會告訴你它是如何例項化_的。但是,如果你詢問Prolog

 ?- abc(_,_,_).

預設情況下,這將不會為真,Prolog需要在資料庫中找到所有三個匿名變數的例項化,例如abc(d,e,f)。由於謂詞abc根本不在資料庫中,因此查詢失敗。你也可以在你的資料庫中使用匿名變數。放置

human(_).

在你的資料庫中意味著任何項,無論它是否已經存在,都是human。因此,查詢

 ?- human(mark).

 ?- human(orange).


在資料庫中存在上述事實的情況下將為真。這裡匿名變數用於宣告所有物件的屬性,而不僅僅是一個物件。如果我們想宣告特定的一組物件具有某個屬性,我們需要規則。下一節將對此進行處理。

以下程式描述了一些城市的公共交通系統

 transport(dresden, tram).
 transport(amsterdam, tram).
 transport(amsterdam, subway).
 transport(new_york, subway).

我們可以詢問Prolog是否存在一個城市同時擁有有軌電車系統和地鐵系統

 ?- transport(A, subway), transport(A, tram).
 A = amsterdam ;
 fail.

(x)在某個地方找到一個家譜,或自己編造一個(真實的更容易檢查你的答案)。使用謂詞woman/1man/1parent/2在Prolog程式中實現部分家譜(大約十個人)。謂詞後面的數字描述了謂詞接受多少個引數。因此,parent/2描述了一個像parent(john, mary)這樣的謂詞。

你可以瀏覽w:Category:Family_trees以獲取合適的家譜。

為以下命令和問題編寫Prolog查詢。如果某些人被多次返回,請不要擔心。我們稍後將瞭解如何處理這種情況。

  1. 列出資料庫中的女性。
  2. 列出資料庫中的兒童。
  3. 列出所有父子組合。
  4. 哪些女性在資料庫中既有父親又有兒子?

你能想到一種方法來顯示資料庫中沒有列出父親的女性嗎?你能描述一下編寫此類查詢需要什麼嗎?

某些練習的答案可以在此處找到:Prolog/Introduction/Answers

參考文獻

[編輯 | 編輯原始碼]
  1. 使用SWI-Prolog時,不確定其他版本是否相同。

下一章:規則


華夏公益教科書