Prolog入门

Prolog入门

http://www.ruanyifeng.com/blog/2019/01/prolog.html

Prolog 语言入门教程

作者: 阮一峰

日期: 2019年1月28日

Prolog 是一种与众不同的语言,不用来开发软件,专门解决逻辑问题。比如,"苏格拉底是人,人都会死,所以苏格拉底会死"这一类的问题。

img

Prolog 就是"逻辑编程"(programming of Logic)的意思。只要给出事实和规则,它会自动分析其中的逻辑关系,然后允许用户通过查询,完成复杂的逻辑运算。

本文简单介绍如何使用 Prolog 语言,主要参考了 xmonader 的教程

一、SWI-Prolog

学习之前,请安装 Prolog 的运行环境 SWI-Prolog,才能运行后面的代码。

img

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 ;

(完)

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 之类的

posted @ 2020-01-14 16:11  别再闹了  阅读(3896)  评论(1)    收藏  举报