• Posts tagged "prolog"

Blog Archives

prolog递归程序设计

Prolog语言知识推理系列文章,不同编程语言有着不同编程视角,JAVA是面向对象,Nodejs是异步回调,R语言是统计算法,Prolog就是知识推理。每种语言都是独特的,如果想把一门语言学好用好,关键是利用语言的特点,做对的事情。

本系列文章主要介绍Prolog语言,从入门安装到知识推理。Prolog是完全不一样的,他没有复杂的程序结构,也不是为了解决算法问题,而是专注于逻辑推理,擅长解决抽象的问题。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/prolog-recursion/

前言

Prolog有简单的语法结构,用来进行事实描述非常容易,在实际编程时,除了有明确的事实定义,还有很多逻辑推理的算法需要来解决,比如递归的程序涉设计。

用prolog的语法,在思考逻辑和程序写法上,与其他高级编程语言有非常大的跨度,需要我们切换大脑思路。我就在尝试中,消耗大量脑细胞,也还是没有完全掌握。

目录

  1. 什么是递归?
  2. 两个数比大小
  3. 循环语句实现
  4. factorial阶乘计算
  5. hanoi游戏推理

1. 什么是递归?

什么是递归?递归是指一种通过重复,将问题分解为同类的子问题,而解决问题的方法。函数直接或间接调用自身的过程称为递归,而相应的函数称为递归函数。使用递归算法,可以很容易地解决某些问题。此类问题的示例包括:汉诺塔(hanoi),树的遍历,图的图的深度优先搜索等。

Prolog的递归实现很奇怪,真的是每种编程语言都有自己的特点,要理解语言的特性并不容易,那么下面就以递归来做举例,让大家也来了解一下,如何用prolog实现递归的逻辑推理过程。

2. 两个数比大小

在recursion.pl文件中,定义bigger()规则,用于比较进行两数比较大操作,返回更大的数字。按照prolog的编程逻辑,我们给bigger规则定义三个参数bigger(A, B, C)。

  • 参数1,用于接收第1个数字。
  • 参数2,用于接收第2个数字。
  • 参数3,用于返回最大的数字。

% 两个数比大小
bigger(X, Y, X) :- X >= Y,!.
bigger(X, Y, Y) .

我们还需要定义,比较大小的规则,设定2条规则。

  • 第1行,用于定义X >= Y时,返回X,最后的!表示不继续向下执行。
  • 第2行,用于定义不满足第1行的条件时,返回Y。

脚本执行:


% X=3, Y=5,则返回X=5
3 ?- bigger(3,5,X).
X = 5.

% X=5, Y=3,则返回X=5
4 ?- bigger(5,3,X). 
X = 5.

好诡异!!我们跟踪一下prolog的程序逻辑,使用trace.命令,按c执行每一步,跳出正在执行的规则按s,完成整个规则计算按n。


% 启动跟踪日志
5 ?- trace.
true.

% 执行语句
[trace] 5 ?- bigger(3,5,X).
   Call: (10) bigger(3, 5, _8002) ? creep   % 进入 bigger(X, Y, X) 规则
   Call: (11) 3>5 ? creep                % 进行 X >= Y 判断
   Fail: (11) 3>5 ? creep                % 进行 X >= Y 判断失败
   Redo: (10) bigger(3, 5, _8002) ? creep   % 继续执行,进入 bigger(X, Y, Y)规则
   Exit: (10) bigger(3, 5, 5) ? creep       % 结束返回
X = 5.

从运行过程跟踪情况来看,2个规则会按顺序执行,第1个规则不满足,则执行第2个规则。

接下来,再执行一下第二语句,让第一个参数为5,第二个参数为3,bigger(5,3,X). 。


% 执行语句
[trace] 6 ?- bigger(5,3,X).                 % 进入 bigger(X, Y, X) 规则
   Call: (10) bigger(5, 3, _90) ? creep     % 进行 X >= Y 判断
   Call: (11) 5>3 ? creep                % 进行 X >= Y 判断成功
   Exit: (11) 5>3 ? creep
   Exit: (10) bigger(5, 3, 5) ? creep       % 结束返回
X = 5.

从运行过程跟踪情况来看,第1个规则满足条件,由于设置了!号,就不进行行第2个规则的执行了。

3. 循环语句实现

在recursion.pl文件中,继续定义loop()规则,用于实现一个循环操作,不需要返回值,实现输入一个数字,倒叙打印出从输入的数字到1的所有连续整数,只有1个参数用于接收输入的数字。


% 循环语句实现
loop(0).  
loop(N) :- 
    N>0, write(N), nl,
    S is N-1, loop(S).  

规则定义:

  • 第1行,当参数为0时,程序结束。
  • 第2行,当参数大于0时,输入当前参数,让参数减一,再递归调用loop()规则

脚本执行:


10 ?- loop(3).  
3
2
1
true .

我们使用trace.命令进行程序跟踪。


% 启动跟踪日志
11 ?- trace.
true.

% 执行语句
[trace] 12 ?- loop(3).              
   Call: (10) loop(3) ? creep        % 进入 loop(N) 规则   
   Call: (11) 3>0 ? creep         % 判断 N>0
   Exit: (11) 3>0 ? creep
   Call: (11) write(3) ? creep       % 输出N
3
   Exit: (11) write(3) ? creep
   Call: (11) nl ? creep

   Exit: (11) nl ? creep
   Call: (11) _7946 is 3+ -1 ? creep   % 执行S=N-1=3-1=2
   Exit: (11) 2 is 3+ -1 ? creep
   Call: (11) loop(2) ? creep          % 进行loop(2)规则
   Call: (12) 2>0 ? creep           
   Exit: (12) 2>0 ? creep
   Call: (12) write(2) ? creep         % 输出2
2
   Exit: (12) write(2) ? creep
   Call: (12) nl ? creep

   Exit: (12) nl ? creep
   Call: (12) _8348 is 2+ -1 ? creep   % 执行S=N-1=2-1=1
   Exit: (12) 1 is 2+ -1 ? creep
   Call: (12) loop(1) ? creep          % 进行loop(1)规则
   Call: (13) 1>0 ? creep
   Exit: (13) 1>0 ? creep
   Call: (13) write(1) ? creep
1                                      % 输出1
   Exit: (13) write(1) ? creep
   Call: (13) nl ? creep

   Exit: (13) nl ? creep
   Call: (13) _8750 is 1+ -1 ? creep    % 执行S=N-1=1-1=0
   Exit: (13) 0 is 1+ -1 ? creep        % 判断 N>0 失败
   Call: (13) loop(0) ? creep           % 进入 loop(0) 规则   
   Exit: (13) loop(0) ? creep           % 结束返回
   Exit: (12) loop(1) ? creep
   Exit: (11) loop(2) ? creep
   Exit: (10) loop(3) ? creep           
true .

这个循环的程序中,我们就用到了递归计算,

4. factorial阶乘计算

factorial阶乘计算这个例子,在我们学习每一种语言时,都会这个问题的求解,而且都是用递归来实现的,那么对于prolog编程来说,我们也来试怎么实现。

在recursion.pl文件中,继续定义factorial()规则,用于实现数字阶乘计算,需要返回值。实现输入一个数字,计算从1到这个数字阶乘结果,并返回。

我们需要定义2个参数,

  • 参数1,用于接收数字。
  • 参数2,用于返回阶乘的计算结果。

% factorial阶乘计算
factorial(0,1).   
factorial(N,F) :-    
    N>0,   
    N1 is N-1,   
    factorial(N1,F1),   
    F is N * F1.  

规则定义:

  • 第1行,当第一个参数 输入数字为0时,直接返回1。
  • 第2行,当第一个参数 N 输入数字大于0时,把输入数字减一,递归调用factorial()规则,获得递归的结果保存在第二个参数F中

脚本执行:

% 计算3的阶乘,返回值为X=6
19 ?- factorial(3,X).
X = 6 .

程序跟踪

[trace] 19 ?- factorial(3,X).
   Call: (10) factorial(3, _3310) ? creep     % 进入 factorial(3,F) 规则
   Call: (11) 3>0 ? creep
   Exit: (11) 3>0 ? creep
   Call: (11) _3834 is 3+ -1 ? creep
   Exit: (11) 2 is 3+ -1 ? creep
   Call: (11) factorial(2, _3924) ? creep     % 进入 factorial(2,F) 规则
   Call: (12) 2>0 ? creep
   Exit: (12) 2>0 ? creep
   Call: (12) _4060 is 2+ -1 ? creep
   Exit: (12) 1 is 2+ -1 ? creep
   Call: (12) factorial(1, _4150) ? creep     % 进入 factorial(1,F) 规则
   Call: (13) 1>0 ? creep
   Exit: (13) 1>0 ? creep
   Call: (13) _4286 is 1+ -1 ? creep
   Exit: (13) 0 is 1+ -1 ? creep
   Call: (13) factorial(0, _4376) ? creep    
   Exit: (13) factorial(0, 1) ? creep         % 进入 factorial(0,1) 规则
   Call: (13) _4468 is 1*1 ? creep            
   Exit: (13) 1 is 1*1 ? creep
   Exit: (12) factorial(1, 1) ? creep         % F返回1= 1*1
   Call: (12) _4606 is 2*1 ? creep            
   Exit: (12) 2 is 2*1 ? creep                % F返回2 = 2*1
   Exit: (11) factorial(2, 2) ? creep
   Call: (11) _3310 is 3*2 ? creep            
   Exit: (11) 6 is 3*2 ? creep                % F返回6= 3*2
   Exit: (10) factorial(3, 6) ? creep
X = 6 .                                       % F返回6

根据程序追踪的递归调用过程,我们也可以用一个图来表达程序在执行过程中的调用关系。

这个计算过程,我们需要用到递归的调用方法,同时需要存储递归返回的结果,用于累计每次计算的值。

5. hanoi游戏推理

汉诺塔(hanoi)一个是数学游戏,它由三根杆和许多不同大小的圆盘组成,可以滑到任何杆上。要求圆盘在一个杆上的排列顺序整齐,大小按升序排列,顶部最小,从而形成圆锥形。

我要解决如何让所有的圆盘,从一个杆按顺序移动到另一个杆,规则:

  • 一次只能移动一个盘子。
  • 每一次操作,取出一个杆子最上面的盘子,并将其放在另一个杆子的顶部或空杆上。
  • 只能将小盘子放在大盘子上面

对于4个盘子的解密过程,可以参考下图。

在recursion.pl文件中,继续定义hanoi()规则,用于实现上面的解密过程,并输出操作步骤。我们定义3个规则。

  • hanoi(),用于启动游戏
  • move(),用于盘子的移动操作
  • inform(),用于输出操作日志

% hanoi游戏推理
hanoi(N) :- 
    move(N, left,  right, centre).

move(0, _, _, _) :- !.
move(N, A, B, C) :-
    M is N-1,
    move(M, A, C, B), print(A, B), move(M, C, B, A).

print(X, Y) :-
    write("move disc from "), write(X), write(" to ") ,write(Y),
    nl.    

脚本执行:


32 ?- hanoi(4).     
move disc from left to centre
move disc from left to right
move disc from centre to right
move disc from left to centre
move disc from right to left
move disc from right to centre
move disc from left to centre
move disc from left to right
move disc from centre to right
move disc from centre to left
move disc from right to left
move disc from centre to right
move disc from left to centre
move disc from left to right
move disc from centre to right
true.

操作日志的过程,就与上面图中手动移动过程是一致的,大家可以用trace.自己跟踪一下程序的运行过程。通过4个小例子,让我快速熟悉一下prolog的编程思路,确实与其他语言都不一样的!有意思!

本文的完整代码,可以在github上找到:https://github.com/bsspirit/prolog-learning/tree/main/04 recursion

同样,本文还是对于prolog的初探,进行逻辑推理,从浅入入深的过程,希望本文能让所有的prolog新手快速上手。

转载请注明出处:
http://blog.fens.me/prolog-recursion/

打赏作者

Prolog软件包管理器

Prolog语言知识推理系列文章,不同编程语言有着不同编程视角,JAVA是面向对象,Nodejs是异步回调,R语言是统计算法,Prolog就是知识推理。每种语言都是独特的,如果想把一门语言学好用好,关键是利用语言的特点,做对的事情。

本系列文章主要介绍Prolog语言,从入门安装到知识推理。Prolog是完全不一样的,他没有复杂的程序结构,也不是为了解决算法问题,而是专注于逻辑推理,擅长解决抽象的问题。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/prolog-packages/

前言

继续尝试使用prolog语言,本文将主要介绍prolog的包管理器。我们在使用prolog来编程的时候,避免不了会用到第三方的软件包,比如文件操作包,数据库包,http请求包,数据解析包,算法包等。所以,掌握包的安装,查看,加载,卸载都是必须的基本操作能力。

目录

  1. prolog包管理器
  2. 包管理的函数使用

1. prolog包管理器

每种编程语言,在启动时内核不可能加载所有的东西,就需要通过模块化的组织方式,把不同的组件进行分步加载。有些模块是官方提供的,有些模块需要有各种第三方包的支持。那么就需要对各种第三方进行统一的管理,允许开发人员方便地使用这些第三方包。

这样子就涉及到,软件包管理的各种使用和操作,包括本地已安装包的管理,远程软件包仓库的查询,软件包的安装,软件包的下载,软件的依赖管理等等操作。

prolog提供了软件包管理的功能,通过prolog_pack.pl的文件接口进行实现。在prolog_pack.pl中提供了多种函数,来帮助用户实现软件的安装使用的过程,可以参考官方介绍 https://www.swi-prolog.org/pldoc/doc/_SWI_/library/prolog_pack.pl

该库(prolog_pack)提供了使您可以检查已安装的软件包,安装软件包,删除软件包等。它由内置的attach_packs补充,该附件使已安装的软件包可用作库。也可以通过运行doc_browser. 函数在查询。


% 启动prolog运行环境
~ swipl

% 查看软件包管理器
?- doc_browser.
% Started server at http://localhost:64976/pldoc/
true 

会自动打开下面网页。

当然,我们更多地是使用交互函数,来对软件包进行管理。

2. 包管理的函数使用

对于包管理器的使用方法,我们有常用的5个操作,查看软件包列表pack_list函数,查看本地已安装软件包pack_list_installed函数,查看本地已安装软件包详细信息pack_info函数,安装软件包pack_install函数,卸载软件包pack_remove函数。

2.1 查看软件包列表pack_list函数

pack_list(+Query)函数,查询所有的软件包服务器,Query不区分大小写地匹配已知和已安装软件包的名称和标题,同pack_search(+Query)函数。

对于每个匹配的软件包,将显示一行。每一行的第一个字母表示,软件包安装状态:

  • p:本地未安装软件包
  • i:本地已安装的软件包,与公开发布的最新版本一致
  • U:本地已安装的软件包,不是最新版本可以升级
  • A:本地已安装的软件包,比公开发布的更新
  • l:本地已安装的软件包,不在公开的服务器上

函数执行,下面会列出所有官方公开的软件包。截止到2021年1月11日,共有326个包。


3 ?- pack_list("").
% Contacting server at https://www.swi-prolog.org/pack/query ... ok
p achelois@0.5.0            - Collection of tools to make writing scripts in Prolog easier.
p aleph@5                   - Aleph Inductive Logic Programming system
p amazon_api@0.0.3          - Interface to Amazon APIs
p ansi_termx@0.0.1          - ANSI terminal operations
p ape@6.7.0                 - Parser for Attempto Controlled English (ACE)
p app@0.1                   - Prolog Application Server
p arouter@1.1.1             - Alternative HTTP path router
p assertions@0.0.1          - Ciao Assertions Reader for SWI-Prolog
p atom_feed@0.2.0           - Parse Atom and RSS feeds
p auc@1.0                   - Library for computing Areas Under the Receiving Operating Charactersitics and Precision Recall curves
p b_real@0.5                - Interface predicates to commonly used R functions.
p bddem@4.3.1               - A library for manipulating Binary Decision Diagrams
p bencode@0.0.1             - Bencoding from BitTorrent protocol
p bibtex@0.1.8              - Parser and predicates for BibTeX files

.. 省略

pack_list函数,也执行模糊匹配查询,查询包含db的包。


4 ?- pack_list("db").
% Contacting server at https://www.swi-prolog.org/pack/query ... ok
p bio_db@3.2                - Access, use and manage big, biological datasets.
p bio_db_repo@20.9.14       - Data package for bio_db.
i chess_db@0.3              - PGN and chess game databases.
p db_facts@0.5              - Common db-tables-as-facts and SQL layer for ODBC and proSQLite.
p fld@0.1.0                 - Easy assess to term args when loading from ODBC or CSV.
p pl_omdb@0.5.0             - API interface to OMDB (Open Movie Database)
p rocksdb@0.8.0             - SWI-Prolog interface to RocksDB
true.

2.2 查看本地已安装软件包pack_list_installed函数
只查看本地已安装包pack_list_installed(),与pack_list/1不同,仅显示本地安装的软件包,并且不建立与Internet的连接。


8 ?- pack_list_installed.
Installed packages (2):

i chess_db@0.3              - PGN and chess game databases.
i real@2.1                  - Integrative statistics with R
true.

我当前本地只安装了2个包,分为是chess_db和real,都是公开发布的最新版本。

2.3 查看本地已安装软件包详细信息pack_info函数
pack_info(+ Pack),查看本地已安装软件包的详细信息。


?- pack_info(chess_db).
Package:                chess_db
Title:                  PGN and chess game databases.
Installed version:      0.3
Installed in directory: c:/programdata/swi-prolog/pack/chess_db
Author:                 Nicos Angelopoulos <http://stoics.org.uk/~nicos>
Maintainer:             Nicos Angelopoulos <http://stoics.org.uk/~nicos>
Packager:               Nicos Angelopoulos <http://stoics.org.uk/~nicos>
Home page:              http://stoics.org.uk/~nicos/sware/chess_db
Download URL:           http://stoics.org.uk/~nicos/sware/packs/chess_db/chess_db-*.tgz
Provided libraries:     chess_db
true.

如果本地没有安装,查看时则失败。


18 ?- pack_info(xsd).
Warning: No pack xsd installed.  Use ?- pack_list(Pattern) to search
false.

提示本地没有安装,先用pack_list()查看一下,是否有这个包。

2.4 安装软件包pack_install函数
pack_install(+Name),安装软件包,先来安装一下xsd包。


?- pack_install(xsd).
% Contacting server at https://www.swi-prolog.org/pack/query ... ok
% Pack `xsd' is already installed @0.2.1
true.

再安装yaml包


?- pack_install(yaml).
% Contacting server at https://www.swi-prolog.org/pack/query ... ok
Select download location for yaml@0.1
   (1) * GIT repository at http://git.cf.ericsson.net/5g-mgmt/yaml.git
   (2)   GIT repository at http://git.cf.ericsson.net/components/yaml.git
   (3)   GIT repository at http://git.cf.ericsson.net/ehonlia/yaml.git
   (4)   GIT repository at https://github.com/honnix/yaml.git
   (5)   Cancel

Your choice? 
% Cloning into 'c:/programdata/swi-prolog/pack/yaml'...
% Contacting server at https://www.swi-prolog.org/pack/query ... ok
% "yaml.git" was downloaded 171 times
Package:                yaml
Title:                  YAML parser
Installed version:      0.1
Author:                 Honnix Liang <hxliang1982@gmail.com>
Maintainer:             Hongxin Liang <hxliang1982@gmail.com>
Packager:               Honnix Liang <hxliang1982@gmail.com>
Home page:              https://github.com/honnix/yaml
Download URL:           https://github.com/honnix/yaml/archive/v0.1.zip
Run post installation scripts for pack "yaml" Y/n? 
% make: Nothing to be done for 'all'.
% make: Nothing to be done for 'check'.
% make: Nothing to be done for 'install'.
true.

安装后的包,都放在了本地的swi-prolog安装目录 C:\ProgramData\swi-prolog\pack 目录下面了。


C:\ProgramData\swi-prolog\pack>dir
 驱动器 C 中的卷是 Windows
 卷的序列号是 28E7-F0A9

 C:\ProgramData\swi-prolog\pack 的目录

2021-01-11  11:34    <DIR>          .
2021-01-11  11:34    <DIR>          ..
2021-01-11  09:49    <DIR>          chess_db
2021-01-11  11:24    <DIR>          Downloads
2020-12-31  16:11    <DIR>          real
2021-01-11  11:26    <DIR>          xsd
2021-01-11  11:38    <DIR>          yaml
               0 个文件              0 字节
               7 个目录 84,370,649,088 可用字节

用tree命令查看,可以看到整个目录的结构


C:\ProgramData\swi-prolog\pack>tree
卷 Windows 的文件夹 PATH 列表
卷序列号为 28E7-F0A9
C:.
├─chess_db
│  ├─data
│  │  └─pgn
│  ├─doc
│  │  └─html
│  ├─examples
│  │  └─short
│  ├─prolog
│  └─src
│      └─lib
├─Downloads
├─real
│  ├─.git
│  │  ├─hooks
│  │  ├─info
│  │  ├─logs
│  │  │  └─refs
│  │  │      ├─heads
│  │  │      └─remotes
│  │  │          └─origin
│  │  ├─objects
│  │  │  └─pack
│  │  └─refs
│  │      ├─heads
│  │      └─remotes
│  │          └─origin
│  ├─c
│  ├─configs
│  ├─doc
│  ├─examples
│  │  └─ws_hist
│  ├─lib
│  │  ├─i386-win32
│  │  ├─x64-win64
│  │  └─x86_64-linux
│  └─prolog
└─yaml
    ├─examples
    └─prolog
        └─yaml

2.5 卸载软件包pack_remove函数
pack_remove(+Name),用于卸载软件包。


?- pack_remove(xsd).
% Removing 'c:/programdata/swi-prolog/pack/xsd' and contents
true.

% 查看已安装软件包
?- pack_list_installed.
Installed packages (3):

i chess_db@0.3              - PGN and chess game databases.
i real@2.1                  - Integrative statistics with R
i yaml@0.1                  - YAML parser
true.

本文的完整代码,可以在github上找到:https://github.com/bsspirit/prolog-learning/tree/main/03 package

同样,本文还是对于prolog的初探,进行逻辑推理,从浅入入深的过程,希望本文能让所有的prolog新手快速上手。

转载请注明出处:
http://blog.fens.me/prolog-packages/

打赏作者

prolog语言安装

Ubuntu实用工具系列文章,将介绍基于Linux ubuntu的各种工具软件的配置和使用。有些工具大家早已耳熟能详,有些工具经常用到但确依然陌生。我将记录我在使用操作系统时,安装及配置工具上面的一些方法,把使用心得记录下来也便于自己的以后查找和回忆。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/prolog-install/

前言

最近在研究逻辑推理的一些问题,无意中发现了一门编程语言prolog就是解决推理问题的。这可太好了,直接勾起了我们的兴趣,马上动手试一下。本文是prolog编程语言的初探,一切从零开始,就让我们揭开prolog的神秘的面容吧。

目录

  1. 什么是prolog语言
  2. 在Ubuntu中安装prolog
  3. 在Win10中安装prolog
  4. 5分钟上手
  5. 躲不过的错误:DWIM could not correct goal

1. 什么是prolog语言

prolog(Programming in logic)是一种面向演绎推理的逻辑型程序设计语言,最早于1972年由柯尔麦伦纳(Colmeraner)及其研究小组在法国马赛大学提出。prolog以处理一阶谓词演算为背景,由于其简单的文法、丰富的表达力和独特的非过程语言的特点,很适合用来表示人类的思维和推理规则,从而一问世就赢得了人工智能研究和应用开发者的广泛兴趣。

Prolog实际上就是一种基于逆向规则的演绎推理技术,只不过对规则和目标的表示有严格的限制.再加上演绎推理控制机制自身的简单性,难以适用于复杂的应用域。

Prolog 语言仅有三种语句:事实(Fact)、规则(Rule)和问题(Question)。

  • 事实,是prolog中最简单的谓词,它和数据库中的记录十分相似。事实用来说明一个问题中已知的对象的性质和它们之间的关系。
  • 规则,由几个互相依赖的简单句(谓词)组成。用来描述事实之间的依赖关系,如:因果关系,蕴含关系,对应关系。
  • 问题,是把事实和规则写入Prolog程序之后,就可以想Prolog询问有关问题的答案,询问的问题就是程序的运行目标。

从语言的描述来看,很抽象、很强大的样子,就让我们为体验一下吧。

2. 在Ubuntu中安装prolog

本文使用的Linux是Ubuntu 20.04.1 LTS Server 64bit的系统,安装Prolog数据库软件包可以通过apt实现。

简单地3条命令就可以实现


# 增加swi-prolog源到配置文件
~ sudo apt-add-repository ppa:swi-prolog/stable

# 更新源
~ sudo apt update

# 安装
~ sudo apt install swi-prolog

查看prolog安装版本,为最新的8.2.3


~ swipl --version
SWI-Prolog version 8.2.3 for x86_64-linux

启动prolog的命令行


~ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.3)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?-

启动后,出现 ?- 这个符号,表示启动正常,进入到了prolog的运行时环境。

3. 在Win10中安装prolog

我们需要从官网下载prolog的window的安装包,最新版本为8.2.3,https://www.swi-prolog.org/download/stable

然后,一路(next)安装就行,安装完成后,我们找到SWI-Prolog的快捷方式启动。

启动后,出现的操作界面和Linux是一致的,我们就可以在这个环境中运行prolog的脚本程序了。

4. 5分钟上手

接下来,我们初步认识一下prolog编程,做一个5分钟上手的小例子。

4.1 语句以 . 为结束标志
在prolog编程中,每条命令执行完,需要输入 . 表示语句运行结束。如果不输入 . 就会默认自动换行处理,第一次是 ?- 提示符,第二行为 | 提示符。

查看当前的运行路径,以.结尾


?- pwd.
% c:/users/bsspi/documents/prolog/
true.

%% 没有输入.,默认自动换换行,再第二行输入 .
?- pwd
|    .
% c:/users/bsspi/documents/prolog/
true.

4.2 切换路径

通常来说,每个项目都会运行在不同路径中,所以我们需要切换工作路径。


?- cd('c:/work/prolog').
true.

%% 查看路径
?- pwd.
% c:/work/prolog/
true.

4.3 语法帮助

查看帮助命令,使用 help(命令). ,分别查看pwd命令和append命令。

查看append命令的帮助


?- help(append).
                                                                          Availability: built-in

append(+File)
    Similar to tell/1, but positions the file pointer at the end of File rather  than truncating
    an existing file. The pipe construct is not accepted by this predicate.

                                Availability: :- use_module(library(lists)). (can be autoloaded)

append(+ListOfLists, ?List)
    Concatenate a list of lists. Is true if  ListOfLists is  a list  of lists,  and List  is the
    concatenation of these lists.

         ListOfLists  must be a list of possibly partial lists 

                                Availability: :- use_module(library(lists)). (can be autoloaded)

append(?List1, ?List2, ?List1AndList2)
    List1AndList2 is the concatenation of List1 and List2
true.

查看pwd命令的帮助


?- help(pwd).
Warning: No help for pwd.
Warning: Use ?- apropos(query). to search for candidates.
true.

4.4 输出到控制台

输出hello world


?- write("hello world").
hello world
true.

连续输出 hello world fens.me


?- write("hello world "),write("fens.me").
hello world fens.me
true.

连续输出 hello world fens.me,中间中nl换行


?- write("hello world "),nl,write("fens.me").
hello world 
fens.me
true.

4.5 加载官方例子
prolog官方为了能够让大家让手方便,提供了一个简单的例子的脚本,我们可以加载这些脚本,看看官方的脚本编写语法。

加载swi软件demo目录下的likes.pl文件


?- [swi(demo/likes)].
true.

查看likes文件中定义的sam都喜欢什么东西,用X标识X为变量,每次只显示一行,按 ; 换行继续显示,可以用来查看sam都喜欢什么东西。


?- likes(sam, X).
X = dahl ;
X = tandoori ;
X = kurma ;
X = chow_mein ;
X = chop_suey ;
X = sweet_and_sour ;
X = pizza ;
X = spaghetti ;
X = chips.

编辑likes.pl文件


?- edit(likes).
true.

会自动弹出一个prolog编辑器,可以看到likes.pl文件的源代码。
我们可以输入一些参数进行判断。


# sam喜欢dahl吗
?- likes(sam,dahl).
true .

# sam喜欢chop_suey吗
?- likes(sam,chop_suey).
true .

# sam喜欢curry吗
?- likes(sam,curry).
false.

查看indian餐列表


?- listing(indian) .
indian(curry).
indian(dahl).
indian(tandoori).
indian(kurma).

4.6 创建自定的命令
首先,需要创建一个本地的文件叫abc.pl。

定义 hello 命令,输出Hello world。


?- edit(file(abc)).
true.

在打开的文件abc.pl中,添加下面内容


hello : format('Hello world ~n').

加载abc.pl文件,输出hello命令。


?- [abc].
true.

?- hello.
Hello world
true.

在运行环境修改hello命令,失败。


?- hello : format('Hello fens.me ~n').
false.

?- hello.
Hello world
true.

4.7 完成退出
在我们完成工作任务后,最后用halt命令进行退出运行环境。


?-  halt.

退出prolog环境。

5. 躲不过的错误:DWIM could not correct goal

第一次使用prolog时,不管从哪里复制的过来的例子,都会出现 ERROR: Unknown procedure: love/2 (DWIM could not correct goal) 的错误。我也试了很多次,才搞明白这个错误的问题。

在prolog的执行过程中,prolog要求所有的自定义命令,都必须是在指定文件中定义,所以当我们任意复制过来的命令,直接在运行环境中执行就会出现这个错误。解决的方法,就是把命令写到文件中,然后加载文件就能解决。

比如下面的例子,我们要创建一个恋爱关系,在c:/work/prolog目录下,新建一个love.pl文件。


~ notepad c:/work/prolog/love.pl

love(zhangxueyou,wangfei).
love(zhangxueyou,zhouhuimin).
love(wangfei,xietingfeng).
love(zhouhuimin,zhangxueyou).
love(xietingfeng,wangfei).
love(xietingfeng,zhouhuimin).
love(liudehua,zhouhuimin).
loves(X,Y):-love(X,Y),love(Y,X).  %恋人
rival_in_love(X,Y):-love(X,Z),not(love(Z,X)),love(Z,Y). %情敌

直接在prolog的运行环境中运行,第一行命令,出现错误 (DWIM could not correct goal)


?- love(zhangxueyou,wangfei).
ERROR: Unknown procedure: love/2 (DWIM could not correct goal)

接下来,我们加载c:/work/prolog/love.pl文件



# 加载文件
?- ["c:/work/prolog/love"].
true.

# 判断是否恋人关系
?- love(zhangxueyou,wangfei).
true .

# 判断是否恋人关系
?- love(liudehua,wangfei).
false.

# 判断是否情敌关系
?- rival_in_love(liudehua,wangfei).
false.

通过上面的操作,我们就完成了prolog语言的初次尝试,prolog安装很简单,语法上有点奇怪。随后我将进一步学习prolog语法,以及能用prolog来解决什么问题的尝试。

希望本文能让所有的prolog新手快速上手,顺利完成prolog语言的初探。

转载请注明出处:
http://blog.fens.me/prolog-install/

打赏作者