39

那么,Prolog,告诉我怎么走

 3 years ago
source link: http://liutos.github.io/2020/09/09/那么,Prolog,告诉我怎么走/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

今年四月左右,我心血来潮地为自己立了一个学习Prolog的目标——对,就是那门以逻辑编程和人工智能为卖点的语言。不仅要学会它的基本用法,还妄想用它像朋友圈广告里的Python那样,用来处理Excel文件中的大数据!

尽管处理大数据是开个玩笑,但学习Prolog的目标是真的。既然要学习一门编程语言,就必须找一本靠谱的教材。在无中生友之后,我选择了

由谭浩强老先生主编的 《Learn Prolog Now》

作为入门读物。

尽管《Learn Prolog Now》的内容一点也不real world,却循序渐进、非常地适合初学者,每一章的结尾还准备了“上机题”。出人意料的是,仅仅在第三章就遇到了不会做的题目。在焦急地苦战一番未果后,我拖着疲惫的身躯搁置了它,继续学习后面的章节。

时隔五个月,我再次尝试解答这道题目。却惊喜地发现,只需要冷静地分析再仔细运用前三章学过的知识,解决这道题目也就是水到渠成的事情了。

所以到底是个什么题?

讲了这么多,是时候揭晓这它的真面目了。由于第三题以第二题为基础,因此一并搬运了过来

aQJbYny.jpg!mobile

感兴趣的朋友也可以直接移步 源网页 查看。

看完上面的题目,只学过主流编程语言的朋友大概会是一头雾水,毕竟无论是代码还是术语,都与平日里使用的大相径庭。我来试着解释一下。像 byCar(auckland, hamilton)byTrain(metz, frankfurt) 这样的代码,用Prolog的术语来讲叫做“事实”。就像数学中的公理一样,它们总是成立的。如果向Prolog提问,它会给出肯定的回答

iUZ7biR.jpg!mobile

byCarbyTrain 被称为“谓词”, aucklandhamilton 则是“原子”。

第二题要求定义 travel/2 ,第三题要求定义 travel/3travel 是谓词的名字,2和3则是它所接受的参数的个数。定义一个谓词就是给出描述它何时成立的“规则”,举个例子,可以定义一个名为 len 的谓词,只有当第二个参数等于第一个参数的长度时才成立

2yEJNze.jpg!mobile

以大写字母开头的标识符(如题目中的 X ,上图中的 TL )是变量,在归一化(unification)时Prolog能够为它们赋值使得查询成立。

鉴于本文不是Prolog的入门教程,各位读者如果想进一步了解Prolog,还请移步《Learn Prolog Now》的相关章节。

先解决第二题吧

讲了这么多,该进入正题了。第二题其实不难,细心的读者应该已经发现,这题可以用递归来解决(就像上文的 len 一样)。

设谓词 travel 的两个参数分别叫做 SE ,各代表起点和终点。显然, travel(S, E) 成立,当且仅当:

  1. 可以从 S 搭乘汽车( byCar )、火车( byTrain ),或飞机( byPlane )抵达 E ,或者;
  2. 存在另一个城市 M ,可以从 S 搭乘汽车、火车,或飞机抵达 M ,并且 travel(M, E) 也成立。

上述算法可以轻松地写成Prolog代码

byCar(auckland,hamilton).
byCar(hamilton,raglan).
byCar(valmont,saarbruecken).
byCar(valmont,metz).

byTrain(metz,frankfurt).
byTrain(saarbruecken,frankfurt).
byTrain(metz,paris).
byTrain(saarbruecken,paris).

byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(singapore,auckland).
byPlane(losAngeles,auckland).

travel(S, E) :- just_go(S, E).
travel(S, E) :- just_go(S, M), travel(M, E).

just_go(S, E) :- byCar(S, E).
just_go(S, E) :- byTrain(S, E).
just_go(S, E) :- byPlane(S, E).

让Prolog告诉咱们这个 travel/2 写得对不对

uqIruui.jpg!mobile

精彩!

你话我猜?

Prolog不仅知道一个查询是否成立,还知道这个查询在什么参数下成立。例如,可以让Prolog告诉咱们,从 valmont 可以抵达哪一些城市,以及哪一些城市可以抵达 auckland

IjuuEvj.jpg!mobile

这正是在接下来的题目中需要发扬光大的能力。

终于来到第三题

第三题所要求的 travel 是一个接受三个参数的谓词,第三个参数由从起点到终点的途径城市构成。设这个新的变量为 R ,那么 travel(S, E, R) 成立当且仅当:

  1. 可以从 S 抵达 E ,并且 Rgo(S, E) ,或者;
  2. 存在另一个城市 M ,以及另一条路径 R2 。可以从 S 抵达 M ,并且 travel(M, E, R2) 成立,并且 Rgo(S, M, R2)

那么如何在规则中描述 R 的结构呢?莫非是像上面的谓词 len 那样,在 :- 的右侧写上形如 R is go(S, M, R2) 这样的代码?

并不是。

借助Prolog强大的模式匹配能力,只需要在 :- 的左边声明 R 的结构即可

byCar(auckland,hamilton).
byCar(hamilton,raglan).
byCar(valmont,saarbruecken).
byCar(valmont,metz).

byTrain(metz,frankfurt).
byTrain(saarbruecken,frankfurt).
byTrain(metz,paris).
byTrain(saarbruecken,paris).

byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(singapore,auckland).
byPlane(losAngeles,auckland).

travel(S, E, go(S, E)) :- just_go(S, E).
travel(S, E, go(S, M, R)) :- just_go(S, M), travel(M, E, R).

just_go(S, E) :- byCar(S, E).
just_go(S, E) :- byTrain(S, E).
just_go(S, E) :- byPlane(S, E).

加载这段代码后,就能让Prolog告诉我们,如何从 valmont 去往 losAngeles

MJvyaa2.jpg!mobile

Prolog不仅找出了题目中所给出的答案(见上图的第二行 X = ),还找出了另外一条可行的路径。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK