用prolog推理家族关系
Prolog语言知识推理系列文章 ,不同编程语言有着不同编程视角,JAVA是面向对象,Nodejs是异步回调,R语言是统计算法,Prolog就是知识推理。每种语言都是独特的,如果想把一门语言学好用好,关键是利用语言的特点,做对的事情。
本系列文章主要介绍Prolog语言,从入门安装到知识推理。Prolog是完全不一样的,他没有复杂的程序结构,也不是为了解决算法问题,而是专注于逻辑推理,擅长解决抽象的问题。
关于作者:
- 张丹,分析师/程序员/Quant: R,Java,Nodejs
- blog: http://fens.me
- email: bsspirit@gmail.com
转载请注明出处:
前言
最近在研究逻辑推理的一些问题,无意中发现了一门编程语言prolog就是解决推理问题的。家族关系是社会生活中一个重要的组成元素,每个人都有自己的家族关系网,这种关系结构并不是很容易地表达,而且人物关系一旦多了,会非常地复杂。
试试prolog的在推理上的强大之处,能否帮我们解决复杂的社会问题的推理计算过程。
目录
- 家族关系定义
- 用prolog表达家族关系
- 推理计算
1. 家族关系定义
我们虚拟一个家族关系,共16个人,8男8女,包括几个不同的维度关系,性别,家长,孩子,结婚,兄弟,姐妹等,如下图所示。
上图注释:
- 性别特征:男为蓝色方形,女为橙色圆形。
- 家长关系:用蓝色箭头表示,箭头的开始为家长,箭头的指向为孩子。
- 婚姻关系:用红色连线表示,两端为结婚的男性和女性。
2. 用prolog表达家族关系
我们首先建立一个prolog的文件叫family.pl,在family.pl中定义关系的定义,如果直接在prolog的运行环境中运行代码代码,则会出现DWIM could not correct goal的错误,错误解决请参考文章prolog语言安装
新建文件family.pl。在window中可以直接在任何文本编辑器中,新建一个文件。
~ notepad c:/work/prolog/02 family/family.pl
根据prolog语言的语法结构,我们需要在编程时,分别定义事实(Fact)、规则(Rule)和问题(Question)。
- 事实,就是把具体的对象进行语言表达和描述。
- 规则,就是建立对象之间的关系。
- 问题,就是用prolog来找答案。
事实:建立对象的性别特征,女性和男性。
% female: a,c,i,k,m,e,g,o % male: b,d,j,l,n,f,h,p female(a). female(c). female(i). female(k). female(m). female(e). female(g). female(o). male(b). male(d). male(j). male(l). male(n). male(f). male(h). male(p).
规则:建立家长关系
% parent parent(a,d). parent(a,j). parent(b,d). parent(c,m). parent(c,n). parent(d,m). parent(d,n). parent(d,g). parent(e,g). parent(e,h). parent(f,h). parent(g,o). parent(i,k). parent(i,l). parent(j,k). parent(j,l).
规则:建立婚姻关系
% married married(a,b). married(c,d). married(i,j). married(e,f). married(o,p).
通过上面的代码,我们就让这个家族关系的事实和基本规则建立完成了。
3. 推理计算
接下来,我们就可以进行关系的计算了。我们只需要提出问题,让程序自动实现推理计算的过程。
首先需要打开prolog运行环境,然后加载刚写的family.pl脚本文件。
# 启动 prolog运行环境 ~ swipl # 设置工作路径 ?- cd("c:/work/prolog/02 family/"). true. # 加载脚本 ?- [family]. true.
问题1:a是谁的家长
?- parent(a,Who). Who = d ; Who = j.
a是我们之前定义好的常量,Who是变量,用Who表达结果。得出a是d和j的家长。
问题2:d是谁的孩子
?- parent(Who,d). Who = a ; Who = b.
通过家长的反向规则,可计算出孩子。
我们可以新建一个规则:建立孩子关系,家长关系的反向关系。
child(X,Y) :- parent(Y,X).
重新加载family.pl文件再运行。
?- child(d,Who). Who = a ; Who = b.
问题3:g的爸爸是谁?
需要满足2个条件,是g的家长,同时是男性。
?- parent(X,g),male(X). X = d .
我们可以新建一个规则:建立父子关系和母子关系。
% mother, father mother(X,Y) :- parent(X,Y),female(X). father(X,Y) :- parent(X,Y),male(X).
g的爸爸和g的妈妈。
% g的爸爸 ?- father(Who,g). Who = d . % g的妈妈 ?- mother(Who,g). Who = e.
问题4:e和f结婚了吗?
结婚是一个双方关系,需要计算e和f是否结婚,同时还需要判断f和e是否结婚。
?- married(e,f). true. ?- married(f,e). false.
我们需要建立f和e的新规则:建立双向的婚姻关系。
% 建立双向的婚姻关系 married(X,Y) :- married(Y,X).
重新加载后,计算f和e的婚姻规则。
?- married(f,e). true .
问题5:谁是亲姐妹,谁是亲兄弟
谁是亲姐妹,设X和Y是亲姐妹,Z是X和Y的家长,X和Y都是女性,X不是Y。
?- parent(Z,X),parent(Z,Y),female(X),female(Y), \+ X=Y. Z = d, X = m, Y = g ; Z = d, X = g, Y = m ; false.
计算得出X=m和Y=g是亲姐妹,她们的家长是Z=d。
那么,谁是亲兄弟?设X和Y是亲兄弟,Z是X和Y的家长,X和Y都是男性,X不是Y。
?- parent(Z,X),parent(Z,Y),male(X),male(Y), \+ X=Y. Z = a, X = d, Y = j ; Z = a, X = j, Y = d ; false.
计算得出X=d和Y=j是亲兄弟,她们的家长是Z=a。
新加新规则:建立兄弟和姐妹的关心。
sister(X,Y) :- parent(Z,X),parent(Z,Y),female(X),female(Y), \+ X=Y. brother(X,Y) :- parent(Z,X),parent(Z,Y),male(X),male(Y), \+ X=Y.
问题6:祖父母,姥姥姥爷,爷爷奶奶
在英语里祖父母似乎是不分的,都叫grandparent,而在汉语里妈妈的父母是姥姥和姥爷,爸爸的父母是爷爷和奶奶,规则有所不同。
直接新规则:
% grandparent grandparent(X,Y) :- parent(X,Z),parent(Z,Y). % yeye,nainai yeye_nainai(Y,X) :- father(Z,X),parent(Y,Z). % laolao,laoye laolao_laoye(Y,X) :- mother(Z,X),parent(Y,Z).
重新加载程序:o的祖父母是谁?
?- grandparent(Y,o). Y = d ; Y = e ; false.
o的姥姥和姥爷是谁?
?- laolao_laoye(Y,o). Y = d ; Y = e.
n的爷爷和奶奶是谁?
?- yeye_nainai(Y,n). Y = a ; Y = b.
其实,可以找的关系还有很多,比如表兄妹,姑姑,婶婶,舅舅,姨夫等,同时我们也可以增加其他维度的内容,比如同在一所大学,谁与谁曾经结婚又离婚了,孩子谁在抚养等多种逻辑上复杂的问题。
本文的完整代码,可以在github上找到: https://github.com/bsspirit/prolog-learning/tree/main/02 family
同样,本文还是对于prolog的初探,进行逻辑推理,从浅入入深的过程,希望本文能让所有的prolog新手快速上手。