一、背景
在上一篇博客https://www.cnblogs.com/wkfvawl/p/12056951.html上,我簡單介紹了一下Prolog的一些基本語法,這篇博客主要通過老師課上講過的一個問題,來體驗一下prolog在邏輯推理上的優勢。
在介紹問題之前,先引入一道Prolog題:
試編寫一個描述親屬關系的Prolog程序,然后再給出一些事實數據,建立一個小型演繹數據庫。
提示:可以以父親和母親為基本基本關系(作為基本謂詞),再由此描述祖父、祖母、兄弟、姐妹以及其他親屬關系。
mother(a,c).
mother(a,d).
mother(c,g).
mother(c,f).
father(b,c).
father(b,d).
father(e,g).
father(e,f).
grandfather(X,Z):-father(X,Z),father(Y,Z).
grandmother(X,Z):-mother(X,Y),mother(Y,Z).
sister(X,Y):-mother(Z,X),mother(Z,Y).
aunt(X,Y):-mother(Z,Y),sister(Z,X).
這里使用SWI-Prolog進行測試
注意,在 Prolog 中一個詞若以小寫開頭,那么它是一個固定值,若以大寫字母開頭,則是一個變量。
繼續定義規則:
family(F,M,C) :-father(F,C), mother(M,C).
提出查詢,比如
prolog 將會求出X可能的取值並輸出到控制台,中間的下划線是一個占位符,不會進行求值。有了上面的基礎知識后,我們就可以利用它來解決邏輯問題了,下面就讓我們來解決“斑馬難題”吧。
二、愛因斯坦邏輯難題
有5間不同顏色的房間,每間住個不同國籍的人,每人有自己喜歡的飲料、香煙和寵物。已知信息:
英國人在紅房間中
西班牙人有一條狗
挪威人住在左邊第一間房里
黃房間中的人在抽庫爾斯牌香煙
抽切斯菲爾德牌香煙的人是養了一只狐狸的人的鄰居
挪威人住在藍房間隔壁
抽溫斯頓牌香煙的人有一只蝸牛
抽幸運牌香煙的人喝橘子汁
烏克蘭人喝茶
日本人抽國會牌香煙
抽庫爾斯牌煙的房間在有匹馬的房間隔壁
綠房間中的人喝咖啡
綠房間在白房間的左邊
中間房間的人喝牛奶
這道題的解題關鍵在於,要以一種清晰的方式將每個房子相關的屬性(顏色、國籍、香煙、寵物、飲料、編號)列出來,前面5個提示中包含了5個國家,那可以利用這一點畫出一個表格,每一行表示一個國家,每一列表示房子的一種屬性。一步步根據提示得到一些推論,將結果填入表格,答案便漸漸清晰起來,使用這種人工方式推理的結果如下圖所示:
雖然我們知道了解題的關鍵,但這個問題仍然需要經過很多步的推導才能得出結果,如果使用 prolog 那得到這個問題的答案就簡單多了,只需要定義好事實和規則,然后向 Prolog 提出問題,邏輯引擎就會為我們查出結果來。
下面是解決這個問題的 Prolog 代碼。
house(A,[A,_,_,_,_]).
house(A,[_,A,_,_,_]).
house(A,[_,_,A,_,_]).
house(A,[_,_,_,A,_]).
house(A,[_,_,_,_,A]).
left(A,B,[A,B,_,_,_]).
left(A,B,[_,A,B,_,_]).
left(A,B,[_,_,A,B,_]).
left(A,B,[_,_,_,A,B]).
middle(A,[_,_,A,_,_]).
first(A,[A,_,_,_,_]).
neighbor(A,B,[A,B,_,_,_]).
neighbor(A,B,[_,A,B,_,_]).
neighbor(A,B,[_,_,A,B,_]).
neighbor(A,B,[_,_,_,A,B]).
neighbor(A,B,[B,A,_,_,_]).
neighbor(A,B,[_,B,A,_,_]).
neighbor(A,B,[_,_,B,A,_]).
neighbor(A,B,[_,_,_,B,A]).
attr(Country,Pet,Color,Drink,Smoke).
all_houses(Houses) :-
house(attr(britsh,_,red,_,_), Houses),
house(attr(spain,dog,_,_,_), Houses),
house(attr(japan,_,_,_,congressCigarettes), Houses),
house(attr(ukraine,_,_,tea,_), Houses),
house(attr(norway,_,_,_,_), Houses),
first(attr(norway,_,_,_,_), Houses),
left(attr(_,_,green,_,_), attr(_,_,white,_,_), Houses),
house(attr(_,snail,_,_,winstonCigarettes), Houses),
house(attr(_,_,yellow,_,coorsCigarettes), Houses),
middle(attr(_,_,_,milk,_), Houses),
house(attr(_,_,green,cafe,_), Houses),
neighbor(attr(norway,_,_,_,_), attr(_,_,blue,_,_), Houses),
house(attr(_,_,_,orange,luckCigarattes), Houses),
neighbor(attr(_,fox,_,_,_), attr(_,_,_,_,chesfieldCigarettes), Houses),
neighbor(attr(_,horse,_,_,_), attr(_,_,_,_,coorsCigarettes), Houses),
house(attr(_,zebra,_,_,_), Houses),
house(attr(_,_,_,water,_), Houses).
在事實部分,將房子看做一個整體,描述了房子在5所房子中、房子的左右關系、中間的房子處於什么位置、第一所房子處於什么位置、房子間的相鄰關系以及每所房子擁有哪些屬性。
規則部分包含了對題目中提示的描述和最終問題的描述,這些定義是為了告訴邏輯引擎,在求值時必須滿足這些條件。
最終的查詢為 all_houses(A) ,prolog 邏輯引擎將會查找出滿足結果的房子數組,注意每所房子由它的屬性組成,這樣最后得到的結果為: