Prolog入門
http://www.ruanyifeng.com/blog/2019/01/prolog.html
Prolog 語言入門教程
作者: 阮一峰
日期: 2019年1月28日
Prolog 是一種與眾不同的語言,不用來開發軟件,專門解決邏輯問題。比如,"蘇格拉底是人,人都會死,所以蘇格拉底會死"這一類的問題。
Prolog 就是"邏輯編程"(programming of Logic)的意思。只要給出事實和規則,它會自動分析其中的邏輯關系,然后允許用戶通過查詢,完成復雜的邏輯運算。
本文簡單介紹如何使用 Prolog 語言,主要參考了 xmonader 的教程。
一、SWI-Prolog
學習之前,請安裝 Prolog 的運行環境 SWI-Prolog,才能運行后面的代碼。
SWI-Prolog 官網有各個操作系統的二進制安裝包,下載即可。Debian / Ubuntu 系統還可以用下面的命令。
$ sudo apt-get install swi-prolog
安裝以后,Linux 系統可以命令行啟動。
$ swipl ?-
然后,就進入了 Prolog 運行環境,?-
是命令提示符。下面是 Hello world 的例子。
?- write("Hello, world"). Hello, world! true.
上面命令輸出 Hello world。
有幾個地方需要注意。Prolog 所有語句的結尾都用一個"點"(.
)表示結束。write()
是打印命令。命令本身就是一個表達式,輸出完成以后,返回值就是true.
,也會顯示出來。
如果想在 Hello world 之間插入一個換行,可以使用nl
命令。
?- write('Hello,'), nl, write('world'). Hello, world true.
退出 SWI-Prolog,可以使用halt
命令,別忘了后面還要加一個點。
?- halt.
二、基本語法
2.1 常量和變量
Prolog 的變量和常量規則很簡單:小寫字母開頭的字符串,就是常量;大寫字母開頭的字符串,就是變量。
?- write(abc). abc true. ?- write(Abc). _3386 true.
上面代碼中,abc
是常量,輸出就是自身;Abc
是變量,輸出就是該變量的值。
2.2 關系和屬性
兩個對象之間的關系,使用括號表示。比如,jack 的朋友是 peter,寫成friend(jack, peter).
。
注意,jack 的朋友是 peter,不等於 peter 的朋友是 jack。如果兩個人都認為對方是朋友,要寫成下面這樣。
friend(jack, peter). friend(peter, jack).
如果括號里面只有一個參數,就表示對象擁有該屬性,比如 jack 是男性,寫成male(jack).
。
2.3 規則
規則是推理方法,即如何從一個論斷得到另一個論斷。
舉例來說,我們定下一條規則:所有朋友關系都是相互的,規則寫成下面這樣。
friend(X, Y) :- friend(Y,X).
上面代碼中,X
和Y
都是大寫,表示這是兩個變量。符號:-
表示推理關系,含義是只要右邊的表達式friend(Y, X)
為true
,那么左邊的表達式friend(X, Y)
也為true
。因此,根據這條規則,friend(jack, peter)
就可以推理得到friend(peter, jack)
。
如果一條規則取決於多個條件同時為true
,則條件之間使用逗號分隔。
mother(X, Y) :- child(Y,X), female(X).
上面代碼中,X
是Y
的母親(mother(X, Y)
)取決於兩個條件:Y
是X
的小孩,X
必須是女性。只有這兩個條件都為true
,mother(X, Y)
才為true
。
如果一條規則取決於某個條件為false
,則在條件之前加上\+
表示否定。
onesidelove(X, Y) :- loves(X, Y), \+ loves(Y,X).
上面代碼中,X
單相思Y
,取決於兩個條件。第一個條件是X
喜歡Y
,第二個條件是Y
不喜歡X
。
2.5 查詢
Prolog 支持查詢已經設定的條件。我們先寫一個腳本hello.pl
。
friend(john, julia). friend(john, jack). friend(julia, sam). friend(julia, molly).
然后在 SWI-Prolog 里面加載這個腳本。
?- [hello]. true.
上面代碼中,true.
是返回的結果,表示加載成功。
然后,可以查詢兩個人是否為朋友。
?- friend(john, jack). true. ?- friend(john, sam). false.
listing()
函數可以列出所有的朋友關系。
?- listing(friend). friend(john, julia). friend(john, jack). friend(julia, sam). friend(julia, molly). true.
還可以查詢john
有多少個朋友。
?- friend(john, Who). Who = julia ; Who = jack.
上面代碼中,Who
是變量名。任意的變量名都可以,只要首字母為大寫。
三、地圖着色問題
下面看看 Prolog 如何解決實際問題。
我們知道,地圖的相鄰區域不能使用同一種顏色。現在有三種顏色:紅、綠、藍。請問如何為上面這幅地圖着色?
首先,定義三種顏色。
color(red). color(green). color(blue).
然后,定義着色規則。
colorify(A,B,C,D,E) :- color(A), color(B), color(C), color(D), color(E), \+ A=B, \+ A=C, \+ A=D, \+ A=E, \+ B=C, \+ C=D, \+ D=E.
上面代碼中,colorify(A,B,C,D,E)
是一個對 ABCDE 五個變量求值的表達式。該表達式為true
的條件是,這五個變量各自為一種顏色,則相鄰的變量不相等。
最后,這兩段代碼合在一起,組成一個腳本map.pl
,再加載這個腳本。
?- [map]. true.
執行表達式colorify(A,B,C,D,E)
,SWI-Prolog 就會將三種顏色依次賦值給變量,測試哪些組合是可能的結果。
?- colorify(A,B,C,D,E). A = red, B = D, D = green, C = E, E = blue; A = red, B = D, D = blue, C = E, E = green ; A = green, B = D, D = red, C = E, E = blue ; A = green, B = D, D = blue, C = E, E = red ; A = blue, B = D, D = red, C = E, E = green ; A = blue, B = D, D = green, C = E, E = red ;
可以看到,計算機給出了6組解,即有6種可行的地圖着色方法。
四、誰是凶手
下面看一個比較有趣的邏輯題。
Boddy 先生死於謀殺,現有六個嫌疑犯,每個人在不同的房間,每間房間各有一件可能的凶器,但不知道嫌疑犯、房間、凶器的對應關系。請根據下面的條件和線索,找出誰是凶手。
已知條件:六個嫌疑犯是三男(George、John、Robert)三女(Barbara、Christine、Yolanda)。
man(george). man(john). man(robert). woman(barbara). woman(christine). woman(yolanda).
為了后面解題的方便,需要把"男人"和"女人"都定義為"人"。
person(X):- man(X). person(X):- woman(X).
六個嫌疑犯分別待在六個房間:浴室(Bathroom)、飯廳(Dining Room)、廚房(Kitchen)、起居室(Living Room)、 儲藏室(Pantry)、書房(Study)。每間房間都有一件可疑的物品,可以當作凶器:包(Bag)、火槍(Firearm)、煤氣(Gas)、刀(Knife)、毒葯(Poison)、繩索(Rope)。
location(bathroom). location(dining). location(kitchen). location(livingroom). location(pantry). location(study). weapon(bag). weapon(firearm). weapon(gas). weapon(knife). weapon(poison). weapon(rope).
下面聲明一條規則,每個房間的人都是不一樣的。
uniq_ppl(A,B,C,D,E,F):- person(A), person(B), person(C), person(D), person(E), person(F), \+A=B, \+A=C, \+A=D, \+A=E, \+A=F, \+B=C, \+B=D, \+B=E, \+B=F, \+C=D, \+C=E, \+C=F, \+D=E, \+D=F, \+E=F.
然后,定義一個表達式murderer(X)
,變量X
就是凶手。該表達式只有滿足以下所有條件,才可能為true
。
murderer(X) :- uniq_ppl(Bathroom, Dining, Kitchen, Livingroom, Pantry, Study), uniq_ppl(Bag, Firearm, Gas, Knife, Poison, Rope),
注意,上面代碼中Bathroom
和Bag
這樣的字符串,都是大寫字母開頭,所以都是變量,代表對應的人。至於具體是誰,就要通過推理得到。
線索一:廚房里面是一個男人,那里的凶器不是繩索、刀子、包和火槍。
man(Kitchen), \+Kitchen=Rope, \+Kitchen=Knife, \+Kitchen=Bag, \+Kitchen=Firearm,
線索二:Barbara 和 Yolanda 在浴室和書房。
woman(Bathroom), woman(Study), \+christine=Bathroom, \+christine=Study, \+barbara=Dining, \+barbara=Kitchen, \+barbara=Livingroom, \+barbara=Pantry, \+yolanda=Dining, \+yolanda=Kitchen, \+yolanda=Livingroom, \+yolanda=Pantry,
線索三:帶包的那個人不是 Barbara 和 George,也不在浴室和飯廳。
\+barbara=Bag, \+george=Bag, \+Bag=Bathroom, \+Bag=Dining,
線索四:書房里面是一個帶繩子的女人。
woman(Rope), Rope=Study,
線索五:起居室里面那件凶器,與 John 或 George 在一起。
man(Livingroom), \+Livingroom=robert,
線索六:刀子不在飯廳。
\+Knife=Dining,
線索七:書房和食品儲藏室里面的凶器,沒跟 Yolanda 在一起。
\+yolanda=Pantry, \+yolanda=Study,
線索八:George 所在的那間屋子有火槍。
Firearm=george,
線索九:Boddy 先生死在食品儲藏室里,那里的凶器是煤氣。
Pantry=Gas, Pantry=X, Gas=X,
線索就是上面這些,然后把寫好的所有表達式放在一起,組成一個完整的腳本crime.pl
,代碼看這里。
加載這個腳本,執行murderer(X)
函數,由於條件復雜,運算時間較長,最終會顯示凶手是誰。
?- [crime]. true. ?- murderer(X). KILLER IS :christine Bathroom: yolanda Dining: george Livingroom: john Pantry: christine Study: barbara Kitchen: robert Knife: yolanda Gas: christine Rope: barbara Bag: john Poison: robert Firearm: george X = christine ;
(完)
https://www.zhihu.com/question/31895071
不知道為什么這么多人批這種聲明式語言。根據我的經驗(我用過它寫東西)和了解,這種語言 Prolog 或變種 Datalog,最近越來越受到關注。
學術上比如,JChord 或 Doop 這種高級的分析工具,企業上如日漸知名的 LogicBlox 公司的諸多產品,都是用它寫的啊。
Oracle 的 research 內部也在用它開發,甚至 LLVM 和它最近也在做結合用作編譯和分析啊(具體是誰我就不說了,一家是內部研究中,一家尚在開發中)。
=========
鑒於有人說 “Prolog 和 Datalog 完全兩回事”,我怕自己因常識錯誤帶來誤導,因此查閱資料,結論是我的立場保持不變:
"The term"Datalog" was used by Maier and Warren in [Computing with Logic, 1988] to denote a subset of Prolog."原話出自,"What you always wanted to know about Datalog", IEEE Transactions on Knowledge and Data Engineering, 1989.
文章中開篇第一句話就是說 Datalog 就是基於 logic programming paradigm 設計的,全文大部分都是在講 Datalog 和 Prolog 的相似性和不同的地方,如 "From the syntactical point of view, Datalog is a subset of Prolog; hence each set of Datalog clauses could be parsed and executed by a Prolog interpreter.However, Datalog and Prolog differ in their semantics. While Datalog, as a simplified version of general Logic Programming [78], has a purely declarative semantic ..."
因此我說 Datalog 是 Prolog 的變種不為過;說 “Prolog 和 Datalog 完全兩回事” 有些不太合適。
有幾位對 Prolog 認識不足, 言論大謬, 特此反駁
SWI-Prolog先看這段 Prolog 程序
packages-R/r_demo.pl at master · SWI-Prolog/packages-R · GitHub
有一系列從 Prolog 呼叫 R 語言的程序, 完全不需要額外宣告什麽 FFI 或中間語言, 就自動生成對應的 R 語言函數, 不只能呼叫 R 語言函數, 連 R 語言的語法都嵌了進去@彭飛
除了上述跟 R 語言合用, 光憑 packages 中官方支持的包, 還可以跟 Java 雙向溝通, 不用啥中間語言可以呼叫 C++, 可以 Constraint Query Language 讀寫 SQL 資料庫, 還可以寫網路應用
以此看來 C 語言之類的不少語言用途都沒比 Prolog 廣, 真要就短處論, C 語言只能算是用來寫作業系統跟系統程序的 DSL, Ada 也只是電子軟體控制系統的 DSL, 都不能算通用語言
Prolog 的全稱是 Programming in Logic, 用途是編程, 不是用來搞證明一類的東西, 卻拿來跟 Coq 用途完全不同的東西比較, 這跟拿 Haskell 這個編程語言跟 Coq 比自動證明有啥差別?
其語言本身也不用來作知識表示, 若要作知識表示另有方法
section('packages/semweb.html')
也比較看看我之前提到的應用, Coq 幾時能做到類似的事情?
看看 SWI-Prolog -- op/3 可知 Prolog 的語法相當自由, Prolog 語法根本就不是劣勢, 連『jhon likes tim.』 這種看起來像英文句子也可以是 Prolog 合法程序, 也藉此能嵌入 R 語言語法
寫個 fibonacci 數列就論斷效能是否過於偏頗? Haskell 也常因為惰性求值被亂用, 常常出現效能比 Python 差的程序, 但 Haskell 效能真的有比 Python 差?
另 Python 的效能也是很差, 但並不影響其成為通用語言, 很大一部分原因是他可用 C 擴展
相較下 Prolog 甚至可以自然地用 C++ 跟 Java 擴展, 而且擴展所需的步驟更少, 為何 Prolog 效能會是問題, Python 就不會是問題?
Python 要是沒有可擴展性, 也沒辦法有用到哪裡去
其實問一個 XYZ 語言為啥沒被 IT 業界廣泛應用, 跟問哲學系教授怎沒成為當紅電視名嘴一樣莫名其妙
現在多數 IT 產業的功能幾乎只剩下讓人手機成癮
要是有裝 Matlab 的話, 可以打開安裝的資料夾, 就會發現裡面有個 Swi prolog 的執行檔, 在無形中幫助科研度過不少難關, JVM 的設計文件裡也有提到使用 Prolog 規划
其存在如同《道德經》所雲:太上,不知有之;
------------------------------------------------------------------------------------------------------
離個題說我自己應用 Prolog 的成果
某親戚的農田需要排班幾個抽水馬達跟電機設備, 還要調控光照周期, 但又不能操勞過度避免燒毀, 反正就是一系列的規划問題
我買個 Rasberry PI 里面裝 Linux 跟 Swi Prolog, 用了 CLP(R) 規划, 輸出 IO pin 控制繼電器
用 Prolog 寫起來着實簡單, 同樣問題要是用 C++ 或 Haskell, 我大概過勞死了
Swi Prolog 的庫很全, 各種規划問題, 另外有 Java, C++ 高階 FFI, 有心應用真可以看下, 應用不需要受限於 IT 之類的