• 粉丝日志首页

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推理家族关系

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

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

关于作者:

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

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

前言

最近在研究逻辑推理的一些问题,无意中发现了一门编程语言prolog就是解决推理问题的。家族关系是社会生活中一个重要的组成元素,每个人都有自己的家族关系网,这种关系结构并不是很容易地表达,而且人物关系一旦多了,会非常地复杂。

试试prolog的在推理上的强大之处,能否帮我们解决复杂的社会问题的推理计算过程。

目录

  1. 家族关系定义
  2. 用prolog表达家族关系
  3. 推理计算

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新手快速上手。

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

打赏作者

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/

打赏作者

2020微软Virtual Azure Community Day-异常检测算法

跨界知识聚会系列文章,“知识是用来分享和传承的”,各种会议、论坛、沙龙都是分享知识的绝佳场所。我也有幸作为演讲嘉宾参加了一些国内的大型会议,向大家展示我所做的一些成果。从听众到演讲感觉是不一样的,把知识分享出来,你才能收获更多。

关于作者

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

转载请注明出处:
http://blog.fens.me/meeting-ms-virtual-community-day-20201203

前言

由于疫情在全球肆虐,原来线下的各种会议都改成了线上的模式。微软及时做了线上的分享的调整,这次有幸参加微软的Virtual Community Day,做一次数据分析的主题演讲。除了我的分享,其他人都是.Net和Azure的主题,希望听众能接受这种跨领域的内容。

目录

  1. 我分享的主题:异常检测算法-自动发现数据中的异常值
  2. 会议体验和照片分享

1. 我分享的主题:异常检测算法-自动发现数据中的异常值

在处理时间序列数据时,经常会观测数据中有一个或几个数值与其他数值相比差异较大,或者在周期型的数据中出现了与周期性不相符的数据分布。通过异常检测算法,可以对不匹配预期的模式或异常数据进行识别,自动发现数据中的离群值、噪声值、偏差值等。

我分享主题:数据分析领域正在发生的变革

数据分析,作为大数据和人工智能的一个分支,正在各领域中发挥着作用。异常检测就是一种常见的,而且落地的一个AI的应用场景。本次我的分享也是从4个方面进行介绍,本次分享的PPT下载

我主要为分四个部分进行介绍:

  • 什么是异常检测?
  • 异常检测算法介绍
  • R语言算法实现
  • 现实场景应用

异常检测(Anomaly detection)是目前时序数据分析最成熟的应用之一,从正常的时间序列中识别不正常的事件或行为的过程。

常见的应用场景包括

  • 金融领域:从金融数据中识别”欺诈案例“,如识别信用卡申请欺诈、虚假信贷等;
  • 网络安全:从流量数据中找出”入侵者“,并识别新的网络入侵模式;
  • 电商领域:从交易数据中识别”恶意买家“,如羊毛党、恶意刷屏团伙;
  • 生态灾难预警:基于对风速、降雨量、气温等指标的预测,判断未来可能出现的极端天气;
  • 工业界:可通过异常检测手段进行工业产品的瑕疵检测,代替人眼进行测量和判断。

举例说明一下,下图就是一组时间序列数据,这些数据有趋势型的、周期型的、平稳型型的,蓝色的线是正常的数据,红色的点代表异常的数据。

  • 左上1图是趋势型的,红色的点,在数据趋势变动的过程中,出现了一种凸起。
  • 左下1图是平稳型的,红色的点,在数据点突然的变大,导致数据不平稳了。
  • 左下2图是周期型的,红色的点,出现的位置都是反周期的,导致数据局部反周期的异常。

这些数据异常的情况,在我们的现实生活中会经常的发生,通过算法来自动识别这样的异常,就可以大大解放人的工作,从而实现AI驱动。

2. 会议体验和照片分享

Virtual Azure Community Day全球直播又来啦,本次大会的官方页面:https://azureday.community/, 微信公众号地址:https://mp.weixin.qq.com/s/L2xDf1JIZsHYwJyEZ0wejA

2.1 会议主题

会议主题:从10:00开始 到 17:15,连续不间断。

2.2 相关照片

我在进行分享时候的屏幕截图,CSDN直播 热度1w 不知道是一个什么水平。

最后,整个分享结束,各位嘉宾都辛苦啦。

微软在越来越放开,融合各种技术,并且自己也在支持多种技术的融合和创新。同时,R语言做为数据分析的主要语言,一定会在各个领域中大有可为。

最后打个小广告:公司招聘!

转载请注明出处:
http://blog.fens.me/meeting-ms-virtual-community-day-20201203

打赏作者