8月にリリースされたばかりのTIOBEランキングのトップ20に、古代の人工知能言語Prologが登場しました!
TIOBEを説明:「そして、さらに驚くべきことに、Prologが15年後に再びトップ20に入るのを目にします...予期せぬ復活を遂げました。」
(2021年8月のTIOBEインデックスによる画像)
10年以上の歳月を経て、このユニークな言語が再び皆の前に現れ、Go!に次ぐランキングになりました。学ぶのに早すぎません!
それがわからない場合は、私の足跡をたどってください。今日は、この異なる「人工知能言語」を理解するために、新鮮で洗練された例から始めます。
さらに、この記事に付属するPrologの紹介ビデオも作成しました。ぜひご覧ください。ビデオがあなたに役立つ場合は、3つのリンクをクリックすることを忘れないでください:)
(ビデオを作るのは本当に疲れます。スクリプトから音楽、編集まで、すべて自分でやりました。サポートしてください:)
プロローグ
like(mercury, kathy). % mercury 喜欢 kathy。
like(kathy, mercury). % kathy 喜欢 mercury。
lover(X, Y) :- like(X, Y), like(Y, X). % 如果X喜欢Y,且Y也喜欢X,则X与Y是恋人。
询问mercury和谁是恋人:lover(mercury, Who).
电脑告诉我 Who = kathy,
这就是Prolog!
复制代码
(このテキストは、プロローグ人工知能言語中国語フォーラムのホームページの目立つ位置から取られています。これは、今日でも存在する古代のWebサイトです)
これは非常に示唆に富む詩です!しかし、これが実際には「プログラミング言語」であるとは想像しがたいかもしれません。
プログラミング言語の開発を振り返ると、いくつかの時期があります。
- 第一世代のプログラミング言語:機械語。0と1を使用してコードを記述します(Weld Wudiが示した長い間失われていた魔法のスキル)。
- 第2世代プログラミング言語:アセンブリ言語
- 第三世代プログラミング言語:C、Java、C ++、C#を代表する高級言語
- 3.5 GL:Python、Lisp 1などの後半の第3世代は、より高度な抽象化を備えており、すでに第4世代の影を持っています。
- 第4世代プログラミング言語:ハードウェア関連の詳細を処理するのではなく、SQL、MATLABなどの問題の記述と問題の解決に焦点を当てた、より高いレベルの抽象化
たぶん、プログラミング言語の小さな友達の理解はここで止まります。しかし、実際には数十年前にそれがありました—
- 第五代编程语言:人工智能语言,这一代编程语言期望计算机能自动求解问题,基于问题所给定的某些限制,交由程序来处理而不需以程序员再投入人力开发程序。
今天说「人工智能」你肯定想到 Python,但 Python 并不是第五代的人工智能语言。而 Prolog 就是这种没落十余年的第五代编程语言之一。
今天我们的重点不是谈为什么第五代编程语言集体没落,而是来学习这种上古语言 Prolog。
Prolog
Prolog 这个词来自 PROgramming of LOGic,也就是逻辑编程的意思。Prolog 不需要你编写程序运行过程,你只要给出事实和规则,它会自动分析其中的逻辑关系。然后你就可以通过查询,让 Prolog 完成复杂的逻辑运算。
SWI-Prolog
Prolog 有非常多的实现,维基百科甚至专门有个词条比较不同的 Prolog 实现:
这里我们采用 SWI-Prolog——一个十分完善、仍在开发、维护的开源实现。在 macOS 中,可以用 brew
安装 SWI Prolog:
$ brew install swi-prolog
复制代码
Debian 系 Linux 也可以用 apt-get
来轻松完成安装。其他系统可以去 官网下载页 寻找对应的二进制文件或者从源码构建。
如果你不想或难以完成安装,SWI-Prolog 还为你准备了 在线版本。
安装完成之后,通过 swipl
命令进入一个和 Python 类似的 SWI-Prolog REPL 环境(SWI-Prolog 的提示符是 ?-
):
$ swipl
?-
复制代码
国际惯例,先写 Hello World:
?- write('Hello, World!').
Hello, World! % write 打印的结果
true. % 返回值
复制代码
注意 Prolog 的语句最后以 .
结尾。
要退出 SWI-Prolog,可以摁 Control-D
或输入 halt.
。(你可以通过键入 apropos(quit).
来找到关于“退出”的主题 halt,然后用 help(halt).
查看如何退出)
Prolog 基础语法
与我们平时接触的其他基于变量的编程语言不同,Prolog 的基础是
- Facts:事实
- Rules:规则
- Queries:查询
这几个概念就是字面意思,如
- 事实:
11 不能被 2 整除。
- 规则:
不能被 2 整除的数是奇数。
- 查询:
11 是奇数吗?
- 结果(答案):
11 是奇数。
- 结果(答案):
在 Prolog 中事实和规则和在一起组成了知识库(Knowledge base),我们把这两者写在后缀名为 .pl
的文件里。
查询写在 REPL 中,基于已知知识库,对某个问题进行逻辑推理,给出答案。
事实
例如,假定我们知道一些事实(字面意思)是:
- Yomogi 和 Yume 相互喜欢;
- Kaneishi 也喜欢 Yomogi。
在 Prolog 中,这些个事实表示为:
like(yomogi, yume).
like(yume, yomogi).
like(kaneishi, yomogi).
复制代码
我们可以把这个写到一个 ./ssss.pl
文件中,但不能写在 REPL 里。
在 Prolog 中,小写字母开头的单词是常量,表示一个对象,如 like
、mercury
、kathy
都不需要预先定义,不需要赋值,直接写即可。
注意,喜欢这种事情是单向的。A 喜欢 B 不代表 B 喜欢 A(如 kaneishi 与 yomogi),所以这里为了表示 yomogi 与 yume 相互喜欢,必须写两句。
我们把 like
称为一种关系,即代表两个或多个对象之间某种相互联系,我们还可一定义只与一个对象有关的事实,这种事实称为属性:
male(yomogi).
female(yume).
female(kaneishi).
复制代码
规则
现在我们制订一个规则:
- 如果 X 喜欢 Y,且 Y 也喜欢 X,则 X 与 Y 就是恋人。
规则在 Prolog 表示为 head :- goals
,即 goals 成立时,head 也就成立。
lover(X, Y) :- like(X, Y), like(Y, X).
复制代码
(这行代码也写到 ./ssss.pl
中)
这行代码表示,如果 like(X, Y)
和 like(Y, X)
都成立,则有 lover(X, Y)
成立。用 Python 语言表示就是:
def lover(X, Y):
return like(X, Y) and like(Y, X)
复制代码
在 Prolog 中的或与非:
A, B
表示「A 与 B」A; B
表示「A 或 B」\+ A
表示「非 A」
查询
前面的事实和规则都是定义,就是我们告诉 Prolog 一些已知信息。查询才是重头戏,就是让 Prolog 帮我们做逻辑题目。
使用 swipl
命令进入 Prolog 环境,然后使用 consult("path/to/xxx.pl")
加载写在 .pl
文件中的代码,来读取已知条件:
$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.4)
?- consult('ssss.pl').
true.
复制代码
或者,也可以用 [xxx].
这个语法来加载 xxx.pl
。
然后,如果我们想知道 yomogi 是否喜欢 yume,就可以问 Prolog:
like(yomogi, yume).
true.
复制代码
结果 true
表示 like(yomogi, yume)
即 yomogi 喜欢 yume 成立。而:
?- like(yomogi, kaneishi).
false.
?- like(yomogi, gauma).
false.
复制代码
false
就说明 yomogi 不喜欢 kaneishi,当然纯情的 yomogi 更不可能喜欢我们没定义过的陌生人 gauma。
前面这几次查询都是询问某个关系是否成立,也就是看看某个「事实」是否存在。而结合已定义 lover
的规则,Prolog 还可以:
?- lover(yomogi, yume).
true.
?- lover(yume, yomogi).
true.
?- lover(yomogi, kaneishi).
false.
?- lover(kaneishi, yume).
false.
复制代码
通过已知的 like 关系(事实),结合我们给的 lover 定义(规则),Prolog 可以推理:
- yomogi 和 yume 是恋人。当然,反过来说“yume 和 yomogi 是恋人”同样成立。
- 因为 yomogi 不喜欢 kaneishi,所以 yomogi 和 kaneishi 不是恋人!
- yume 和 kaneishi 之间没有任何一方喜欢的关系,所以两人也不是恋人。
如果只是这样,只会判断个对错,那 Prolog 就太弱了。在 Prolog 查询中还可以使用变量。
例如,我们想要知道蓬 yomogi 喜欢谁(Who)?就可以问 Prolog:
?- like(yomogi, Who).
Who = yume.
复制代码
这里 Who
是一个变量,Prolog 会求解查询中的变量,这里得到的结果是 Who = yume
,也就是说 yomogi 喜欢 yume。
这里 Prolog 所做的操作其实就是解个方程,得到使查询结果为真的变量值:like(yomogi, Who) = true
得到 Who=?
。
Prolog 中大写字母开头的单词是变量。(注意,Prolog 中变量大写、常量小写,和我们通常的 C 语言编程习惯是反的。)如果写错了大小写,意思就不对了,尝试查询:
?- like(yomogi, who).
false. % yomogi 不喜欢 who 这个人,咱也不知道 who 是谁
复制代码
如果我们要查询 「yume 是否喜欢 yomogi」,而把 yume 写成了大写的 Yume,就变成了查询「喜欢 yomogi 的人是谁」:
?- like(Yume, yomogi). % Yume 是个变量,不是 yume 这个人
Yume = yume ; % 摁 tab 键或摁 ; 键显示下一个结果
Yume = kaneishi.
复制代码
这里 Yume 等于 Who,只是个变量,表示喜欢 yomogi 的人,而不是南同学前面定义中的 yume 这个人。根据我们一开始定义的事实,yume 和 kaneishi 都喜欢 yomogi,所以这里 Yume (Who)就可以是 yume 或 kaneishi。
对于这种有多个结果的查询。SWI-Prolog 默认每次只显示一个结果,然后等待,我们需要摁下 ;
(表示「或」,还记得吗)然后再显示下一个结果。(这里也可以摁 tab
键)
这里还想补充一点,Prolog 中有一个很有用的谓词 listing
,可以列出某种关系的全部事实,或查看规则的定义:
?- listing(like).
like(yomogi, yume).
like(yume, yomogi).
like(kaneishi, yomogi).
?- listing(lover).
lover(X, Y) :-
like(X, Y),
like(Y, X).
复制代码
例:苏格拉底会不会死?
利用前面这些知识,就可以解决很多逻辑问题了,例如,已知:
- 苏格拉底是人
- 人都会死
所以可以退出结论:苏格拉底会死。
用 Prolog 来解决这个问题:
person(socrates). % 事实
mortal(X) :- person(X). % 规则
---
% 查询
?- mortal(socrates). % 苏格拉底会死吗
true.
?- mortal(X). % 谁会死
X = socrates
复制代码
如果你觉得一个完整的程序不能只包括逻辑运算部分,还必须拥有输入输出,那么,结合 hello world 中的 write,我们可以实现:
person(socrates).
person(plato).
person(aristotle).
mortal(X) :- person(X).
mortal_report :-
write('Known mortals are:'), nl, mortal(X), write(X), nl. % nl 是换行
复制代码
然后再解释器中调用:
?- mortal_report.
socrates
plato
aristotle
复制代码
这就得到了一些生命有限的凡人。
利用这种「逻辑编程」你还可以写成更多有趣的例子。如果你觉得这个例子已经理解不能了,那我推荐你重新学习一下基础的《数理逻辑》(很多《离散数学》书的第一章)。
到这里,我们其实只是看到了 Prolog 的最简单的用法,但完全没有接触到 Prolog 中真正强大的地方,例如递归,这些就写另一篇更深入的文章来进一步学习了,——不过我们现在这篇文章到此就结束了。
参考
[1] Blackburn, Patrick and Bos, Johan and Striegnitz, Kristina. Learn Prolog Now!. College Publications. 2006
[2] 阮一峰. Prolog 语言入门教程. 阮一峰的网络日志. 2019
[3]SWIプロローグ。はじめに。
[4]Draveness。PrologFundamentals <1> .draveness.me、2015
[5]作者不明。プロローグ入門チュートリアル。釣りとZhuxuanの聴取。2004
(前回の記事は、10年近く前にPrologを学んでいたときに読んだ記事でした。とてもわかりやすかったです。しかし、今ではこの記事の再版とこの記事の元の英語訳がインターネット上にあります。この貴重な中国語の翻訳を見つけるのは難しいですが、現在、Ding Diao Ting Zhu XuanというWebサイトからのものである可能性がありますが、このWebサイトは閉じられており、archive.orgのバックアップはすべて3xxエラーです。時間があるときの考古学)
- Lisp:Lispは第5世代のプログラミング言語であると言う情報源もありますが、私が学んだLispによると、Lispは第5世代の定義に同意しませんが、第5世代の言語はLispで実装できます。↩