• Prolog 语言入门教程——果然这个东西用于逻辑推理很强大啊,但是如何将这些专家知识表达为逻辑语言,需要定义


    Prolog 语言入门教程

    作者: 阮一峰

    日期: 2019年1月28日

    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 ;
    

    (完)

  • 相关阅读:
    Android WindowManager和WindowManager.LayoutParams的使用以及实现悬浮窗口的方法
    Android 自定义控件之圆形扩散View(DiffuseView)
    Android线性渐变
    Android Drawable之getIntrinsicWidth()和getIntrinsicHeight()
    Android 用Handler和Message实现计时效果及其中一些疑问
    CentOS6.5下nginx-1.8.1.tar.gz的单节点搭建(图文详解)
    Zeppelin的入门使用系列之创建新的Notebook(一)
    hadoop报错java.io.IOException: Incorrect configuration: namenode address dfs.namenode.servicerpc-address or dfs.namenode.rpc-address is not configured
    ubuntu系统里vi编辑器时,按方向箭头输入是乱码的ABCD字母?(图文详解)
    VirtualBox里如何正确安装增强工具(图文详解)
  • 原文地址:https://www.cnblogs.com/bonelee/p/14864298.html
Copyright © 2020-2023  润新知