慣性聚合 高效追蹤和閱讀你感興趣的部落格、新聞、科技資訊
閱讀原文 在慣性聚合中打開

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

阮一峰的网络日志

科技爱好者周刊(第 396 期):互联网通信的替代方案 科技爱好者周刊(第 396 期):互联网通信的替代方案 - 阮一峰的网络日志 科技爱好者周刊(第 395 期):软件开发的第三种方式 科技爱好者周刊(第 395 期):软件开发的第三种方式 - 阮一峰的网络日志 科技爱好者周刊(第 393 期):脑腐状态 科技爱好者周刊(第 392 期):axios 投毒与好莱坞式骗术 科技爱好者周刊(第 391 期):AI 的贫富分化 科技爱好者周刊(第 390 期):没有语料,大模型就是智障 套壳中国大模型撑起500亿美元估值?扒一扒 Cursor 的"套壳"疑云 科技爱好者周刊(第 389 期):未来如何招聘程序员 科技爱好者周刊(第 388 期):测试是新的护城河 零安装的"云养虾":ArkClaw 使用指南 科技爱好者周刊(第 387 期):你是领先的 科技爱好者周刊(第 386 期):当外卖员接入 AI 字节全家桶 Seed 2.0 + TRAE 玩转 Skill 科技爱好者周刊(第 385 期):马斯克害怕中国车企吗? 智谱旗舰 GLM-5 实测:对比 Opus 4.6 和 GPT-5.3-Codex 科技爱好者周刊(第 384 期):为什么软件股下跌 科技爱好者周刊(第 383 期):你是第几级 AI 编程 Kimi 的一体化,Manus 的分层 科技爱好者周刊(第 382 期):独立软件的黄昏 AI native Workspace 也许是智能体的下一阶段 科技爱好者周刊(第 381 期):中国 AI 大模型领导者在想什么 科技爱好者周刊(第 380 期):为什么人们拥抱"不对称收益" 科技爱好者周刊(第 379 期):《硅谷钢铁侠》摘录 我如何用 AI 处理历史遗留代码:MiniMax M2.1 升级体验 科技爱好者周刊(第 378 期):预测是新的互联网热点 科技爱好者周刊(第 377 期):14万美元的贫困线 科技爱好者周刊(第 376 期):太空数据中心的争议 科技爱好者周刊(第 375 期):一扇门的 Bug 终于有人做了 Subagent,TRAE 国内版 SOLO 模式来了 科技爱好者周刊(第 374 期):6GHz 的问题 VS Code 使用国产大模型 MiniMax M2 教程 科技爱好者周刊(第 373 期):数据模型是新产品的核心 国产大模型接入 Claude Code 教程:以 Doubao-Seed-Code 为例 科技爱好者周刊(第 372 期):软件界面如何设计 大模型比拼:MiniMax M2 vs GLM 4.6 vs Claude Sonnet 4.5 科技爱好者周刊(第 371 期):一个乐观主义者的专访 科技爱好者周刊(第 370 期):正确的代码高亮 错误处理:异常好于状态码 科技爱好者周刊(第 369 期):Tim 与罗永浩的对谈 科技爱好者周刊(第 368 期):不要这样管理软件团队 一天之内,智谱和 Anthropic 都发了最强编程模型 科技爱好者周刊(第 367 期):Nano Banana 的几个妙用 科技爱好者周刊(第 366 期):旧金山疯狂的 AI 广告 科技爱好者周刊(第 365 期):流量变现正在崩塌 科技爱好者周刊(第 364 期):最难还原的魔方 科技爱好者周刊(第 363 期):最好懂的神经网络解释 科技爱好者周刊(第 362 期):GitHub 工程师谈系统设计 科技爱好者周刊(第 361 期):暗网 Tor 安全吗?
Prolog 語言入門教程
阮一峰 · 2019-01-28 · via 阮一峰的网络日志

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).

上面代碼中,XY都是大寫,表示這是兩個變量。符號:-表示推理關係,含義是隻要右邊的表達式friend(Y, X)true,那麼左邊的表達式friend(X, Y)也為true。因此,根據這條規則,friend(jack, peter)就可以推理得到friend(peter, jack)

如果一條規則取決於多個條件同時為true,則條件之間使用逗號分隔。


mother(X, Y) :- child(Y,X), female(X).

上面代碼中,XY的母親(mother(X, Y))取決於兩個條件:YX的小孩,X必須是女性。只有這兩個條件都為truemother(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),

注意,上面代碼中BathroomBag這樣的字符串,都是大寫字母開頭,所以都是變量,代表對應的人。至於具體是誰,就要通過推理得到。

線索一:廚房裡面是一個男人,那裡的兇器不是繩索、刀子、包和火槍。


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 ;

(完)