那迷人的被遺忘的語言:Prolog

創宇前端發表於2018-08-13

在進入正題之前,我們先一起思考一個問題:當我們在程式設計時,我們在做什麼?

看到這個問題,有些同學可能會第一時間要回答:

  • 在聽音樂
  • 在打字
  • 在發呆
  • 在裝作很忙的樣子
  • ……

呃,我不是那個意思,說認真的:當我們在程式設計時,我們在做什麼?

我們在把所想的「解決方案」轉換成計算機可理解的「計算機語言」。

JavaScript, Python, Java, Ruby, C++ …… 等等等等,哪怕是世界上最好的語言 PHP,也大抵如此:「解決方案」->「計算機語言」

但你有沒有想過反過來?「計算機語言」->「解決方案」 這樣,我們使用「計算機語言」描述問題,而計算機來告訴我們「解決方案」?

或許你會想到人工智慧、深度學習等等概念,但那些熱門話題不是我們今天要討論的。

今天,我們來一起聊聊那迷人的被遺忘的語言:Prolog.

Prolog 簡介

Prolog(Programming in Logic)是一種邏輯程式語言。它建立在邏輯學的理論基礎之上, 誕生與 1972 年,最初被運用於自然語言等研究領域,距今 46 年曆史。當下火熱的 JavaScript 語言 23 歲,年齡剛好是 Prolog 的一半。

說到 Prolog 是一種邏輯程式語言,那麼跟一般的函式式語言有什麼區別呢?答:完全不是一碼事情。在 Prolog 裡,最基本的做法是先描述事實(定立物件與物件之間的關係),然後用詢問目標的方式來查詢各種物件之間的關係,系統會自動進行匹配、回溯,並給出答案。

舉個例子:

我們使用下面的語法來表述一個事實

handsome(ergou).
複製程式碼

這個語句描述了一個事實:二狗 (ergou) 是無比帥氣 (handsome) 的,然後以句號 (.) 結束,表明這個事實描述完畢,不容置疑。

然後接下來我們在 Prolog 的控制檯 (REPL) 裡就會得到如下的結論:

?- handsome(X).
X = ergou.

?-
複製程式碼

其中 ?- 開頭的是我們手動輸入的,其他的是 Prolog 返回的。上面的那些操作是:

  1. 我們首先問 Prolog:誰是世界上最帥的人?handsome(X). 其中 X 是大寫字母開頭,在 Prolog 裡所有大寫字母開頭的都是變數。
  2. 然後 Prolog 回答說:X = ergou. ,當然是二狗了!

以上,應該可以算作 Prolog 基本思路的一個演示。你可以去下載適用於你的 Prolog 環境,然後在自己的電腦上把玩一下。

建議使用 SWI Prolog(本文的所有演示程式碼都是在 SWI Prolog 進行的操作):www.swi-prolog.org/

安裝完畢之後,執行下面三步,即可復現上面的結果:

  1. 將事實寫入檔案
echo 'handsome(ergou).' > fact.prolog
複製程式碼
  1. 啟動 Prolog REPL 並載入既定事實:
swipl fact.prolog
複製程式碼
  1. 在 Prolog REPL 內輸入以下語句並回車:
handsome(X).
複製程式碼

誰是我大爺?

剛才的例子是我們描述了一個事實,然後又讓 Prolog 告訴了我們這個事實,好像沒有太大的用途,接下來我們試著做一下別的事情:

檔名:grandfather.prolog

father(yigou,ergou).
father(linggou,yigou).

grandfather(X,Z) :- father(X,Y), father(Y,Z).
複製程式碼

看了上面的程式碼,應該可以理解到,我們是描述了三個事實:

  1. 二狗 (ergou) 的父親是 一狗 (yigou)
  2. 一狗 (yigou) 的父親是零狗 (linggou)
  3. X 是 Z 大爺的前提條件是:X 是 Y 的父親,Y 是 Z 的父親

有了這些事實,我們可以幹什麼?可以找到二狗爺爺啊!

載入既定事實

swipl grandfather.prolog
複製程式碼

執行詢問語句

?- grandfather(X,yigou).
false.

?- grandfather(X,ergou).
X = linggou.

?-
複製程式碼

可以看到一狗 (yigou) 是沒有爺爺的,二狗 (ergou) 的爺爺是零狗 (linggou) 。

我和小紅的共同愛好是什麼?

像剛才那個例子,我們再舉一個來加深一下這種思維方式:

檔名:music.prolog

listen(ergou, bach). % 二狗 聽 巴赫
listen(ergou, beethoven). % 二狗 聽 貝多芬
listen(ergou, mozart). % 二狗 聽 莫扎特
listen(xiaohong, mj).  % 小紅 聽 邁克爾·傑克遜
listen(xiaohong, dylan). % 小紅 聽 鮑勃·迪倫
listen(xiaohong, bach). % 小紅 聽 巴赫
listen(xiaohong, beethoven). % 小紅 聽 貝多芬
複製程式碼

上面這些程式碼描述了二狗和小紅的聽歌品味,現在如果二狗想要找出自己與小紅在聽歌品味上的共同之處,該怎麼辦?

載入既定事實

swipl music.prolog
複製程式碼

執行詢問語句

?- listen(ergou, X),listen(xiaohong,X).
X = bach ;
X = beethoven ;
false.

?-
複製程式碼

我們在載入了既定事實之後,開始詢問:有什麼音樂是二狗 (ergou) 和小紅 (xiaohong) 都有在聽的呢?

?- listen(ergou, X),listen(xiaohong,X).
複製程式碼

然後我們得到了下面的答案:

?- listen(ergou, X),listen(xiaohong,X).
X = bach
複製程式碼

如果你有在跟著文章手動操作,如果你是個細心的同學,你會發現這次詢問之後游標停留在了 X = bach 這裡,而不是換行顯示出 ?-。這是為什麼呢?

這時候 Prolog 是在說:問題還有別的解答,按下 ; 尋求更多,按下 Enter 表示接受當前答案。在上邊了例子中,我們持續按下 ; ,直到返回 false 才停下來。

於是我們得到了二狗和小紅共同欣賞的音樂,是巴赫和貝多芬。

雞兔同籠問題

接下來我們來一個經典問題:

今有雉兔同籠,上有三十五頭,下有九十四足,問雉兔各幾何?

—— 《孫子算經》

翻譯成白話文:

有若干只雞兔同在一個籠子裡,從上面數,有35個頭,從下面數,有94只腳。問籠中各有多少隻雞和兔?

停!快停止思考!

如果你在使用 JavaScript、Python 等等語言的思路去嘗試程式設計解決問題的話,趕緊停下來,現在我們要用描述既定事實的方式,讓 Prolog 給我們答案!

檔名:chicken-and-rabbits.prolog

% 首先,我們引入一個 clpq 的庫來幫助我們進行運算子描述
:- use_module(library(clpq)).

% 然後,我們定義事實:腦袋的總數量(H) 應當等於 雞的總數量(C) 加上 兔子的總數量(R)
head(C,R,H) :- {H = C + R}.

% 然後,我們定義事實:腳的總數量(F) 應當等於 雞的總數量(C)乘以二 加上 兔子的總數量(R)乘以四
foot(C,R,F) :- {F = C*2 + R*4}.
複製程式碼

我們定義好了事實,那麼接下來讓我們去詢問 Prolog 答案:

載入既定事實

swipl chicken-and-rabbits.prolog
複製程式碼

執行詢問語句

?- head(C,R,35),foot(C,R,94).
C = 23,
R = 12.
複製程式碼

答:雞有 23 只!兔有 12 只!

哈哈哈哈,沒錯!再也不需要去絞盡腦汁思考怎麼告訴計算機去計算啦!只需要告訴 Prolog 既定事實,剩下的,就是提出正確的問題啦!

古老的 Prolog 在電腦科學中的逐漸退熱,必然有對應的原因,但這種語言所特有的思維方式,也不妨去探尋一下。在當下的程式設計氛圍裡,或許 Prolog 就像是夏日的一杯加冰檸檬水,給我們帶來涼爽和清新呢!

好了,關於 Prolog 的討論,我們今天就到這裡了,多謝各位!

還有更多關於 Prolog 的資料,感興趣可以繼續閱讀:


文 / 王二狗 職業程式設計師,週末愛好者

編 / 熒聲

本文已由作者授權釋出,版權屬於創宇前端。歡迎註明出處轉載本文。本文連結:knownsec-fed.com/2018-08-09-…

想要看到更多來自知道創宇開發一線的分享,請搜尋關注我們的微信公眾號:創宇前端(KnownsecFED)。歡迎留言討論,我們會盡可能回覆。

那迷人的被遺忘的語言:Prolog

感謝您的閱讀。

相關文章