需要擅长于使用pyke中的in规则

前面在pyke引擎的总结文章中多次提到pyke是为了寻找一个集合中的组合方式而构建的推理引擎。在一种方式里,我们可以通过kfb文件或者_assert,add_universal_fact等方法去声明很多事实。但在构建一些规则的时候,我们需要一个模式去在一些其他方式构建的集合中去遍历。例如我们需要某一个对象遍历从0到10之间所有的数字,来检验一些规则。在这种情况下,我们就需要使用$pattern in python_iterable这样的方式来创建遍历集合了。使用规则示例如下:

test
        use test($pair)
        when
                $first in [0,1,2,3,4]
                $second in ['a', 'b', 'c']
                $pair = str($first) + '_' + $second


测试结果:(with engine.prove_goal(<rule_base>.test($pair)) as gen: ....)

result: {'pair': '0_a'}
result: {'pair': '0_b'}
result: {'pair': '0_c'}
result: {'pair': '1_a'}
result: {'pair': '1_b'}
result: {'pair': '1_c'}
result: {'pair': '2_a'}
result: {'pair': '2_b'}
result: {'pair': '2_c'}
result: {'pair': '3_a'}
result: {'pair': '3_b'}
result: {'pair': '3_c'}
result: {'pair': '4_a'}
result: {'pair': '4_b'}
result: {'pair': '4_c'}

pyke推理引擎关于*$pattern的注意事项

定义

*$pattern在pyke中用来匹配不能确定长度的tuple模式。

必须出现在tuple模式匹配中

根据这个定义,我们首先得出第一条规则,*$pattern必须出现在tuple模式匹配之中。例如:

use someRule($value0, $value1, ($inValue0, *$theRest))

并且这样的方式不能出现在非tuple的匹配中。例如下面这样:

use someRule($value0, $value1, *$theRest)  #不能这样使用

必须出现在tuple模式匹配的后面

pyke解释器在很大程度上是参照python的,*$pattern只能出现tuple的最后。像下面这样是不行的:

use someRule($value0, $value1, ($inValue0, *$theRest, $theLast))

在规则内部对*$pattern赋值时只能用tuple赋值

非tuple类型的数据给pattern赋值是会报错的。看下面示例

test
        use test(($theOne, *$theRest))
        when
                $theOne = 10
                $theRest = (1,2,3,4)  #正确
                $theRest = (1,)     #正确
                $theRest = (1)       #错误
                $theRest = 1         #错误

使用pyke推理引擎的一些原则

推理引擎解决的基本问题是对一些命题的排列组合方式做试探的问题。它可以对很多事实分层次,分逻辑层面做组合问题。虽然说在pyke系统用的krb里面我们可以利用goal来实现很多非排列组合的问题,例如构建一些运算函数等。但是,随着使用的深入体验,我发现并不适合做。很多该是由程序代码来完成的任务还是交给程序代码来做更合适。我是基于以下理由给出此结论:

python对静态代码块的嵌套有限制

python或者这很多编程语言是对静态代码块的嵌套做过限定的。而每一次bc的使用都会引入一个新的嵌套代码块。如果在一个bc里面调用多个bc很容易就会让pyke生成的python代码超出python静态嵌套限制。要知道python只允许嵌套20个代码块的深度。而每一个bc规则的嵌套,会引入2个以上的静态代码块。一个bc规则中,嵌套8个以上bc规则,就会开始危险起来了。

直接使用python代码可以应用很多的python资源

bc规则的设定是用来回答这样的问题的:在某某集合中,是否能找到满足某某条件的组合? 例如,在一些列路径的选择组合中,能否找到一种组合走出迷宫?在一系列数据库的检索语言中,能否找到合适的组合来检索我们需要的内容?如果我们需要证明的问题是,a和b两个对象是否满足某个条件?那么这个问题就不适合用bc规则来写了。因为在krb文件中直接使用python可以调用大量的python资源,尤其是krb支持bc_extra的模式。我们甚至可以专门写python库,然后再在bc_extra中import这些python库。要知道在bc中,我们甚至都没有直接使用if, else。

结论

推理引擎是个很好的东西,在应用它的时候,我们要发挥它的长处来使用,而不是滥用它。基本使用逻辑是,首先构建推理事实库,然后再构建用于寻找这些事实库中满足某些规则的排列组合的问题。并且我们要用python代码来丰富bc规则的能力,然bc代码块能够对一些非排列组合验证性的问题来做出判断。

pyke推理引擎的一些注意事项

pyke是一个与prolog类似的纯python版本的推理引擎。这一类推理引擎比较适合用在一些需要处理大量基于规则来做判断或者决策的应用。规则成为了这类系统里面最重要的东西。有人说,什么东西是软件架构?最重要的东西就是你的软件架构!因此,在遇见这类由规则来主导整个应用的问题的时候,规则管理系统就变成了这个应用的架构。现实世界中很多问题都是这一类应用。因此,一些推理引擎就应运而生了。人们最早可能从80年代开始就有推理引擎的意识了。如果我们能够较好的应用推理引擎,就可以使我们能够脱身于编写繁杂的推理逻辑代码。例如对一系列数据的遍历,if-else判断,等等。有了基于规则而推理的引擎,我们就可以把重心放在想办法将已知条件抽象成事实,将逻辑推理抽象成规则的任务上。这就让我们可以放开推理部分的构建,能够

大幅度缩短开发周期。

我花了一些时间去阅读pyke的文档,从中基本理解了pyke的应用逻辑和语法。并开始尝试了自己使用pyke推理引擎来做一些实际上的小测试。下面陈列一些在这段时间内,我发现的需要特别注意的一些事项:

知识库文件名必须不同名(后缀名不算)

在pyke的框架下,初始化推理引擎的时候会指定一个目录。然后pyke会在那个目录下递归地寻找所有的*.kfb, *.krb和*.kqb文件,并把这些文件中记录的fact,rule和question都编译或者准备好。这些文件统称为pyke的知识库。并且pyke会以这些文件名(不包含后缀)为该知识库的名称。正因为如此,在pyke遍历的过程中如果遇见两个文件名相同的时候就会产生冲突。哪怕是后缀名不同都不行。例如base.kfb和base.krb同时存在是不允许的。

关于知识库实例的归属问题

在pyke的框架下每一个知识库实例都是属于某个知识库之下的。它们具有通用的从属关系。这一点在不同类型的知识库的体现方式有所不一样。

在.kfb中的每个事实实例都是属于与该.kfb文件名对应的那个知识库下的。例如base.kfb中的所有实例都是属于 base 名下的。我们假设 base.kfb 中有一条fact是sonOf(david, jim),那么在整个框架下定位这一条fact时,则需要用base.sonOf来指定。要注意的是,在base.kfb文件中,并没有显示的指明sonOf是属于base知识库的。

在.krb中的规则实例用来表述与知识库的从属关系时又不一样。在正向推理的规则中都要写明每一个知识库实例所属的知识库。例如:

son_of
    foreach
        family.son_of($child, $father, $mother)
    assert
        family.child_parent($child, $father, father, son)
        family.child_parent($child, $mother, mother, son)

在这段规则中,每一条前提和结论都显式的写名了其所属的知识库为family。

但是在逆向推理的规则中,我们在use声明中省去知识库的标识符。并且在when中也可以选择性的缺省知识库的标识符。例如:

parent_and_child
    use child_parent($child, $parent, (), $parent_type, $child_type)
    when
        child_parent($child, $parent, $parent_type, $child_type)

这一个反向推理的规则中,use下的child_parent没有像前面正向推理一样写明知识库。而且在use下是必须省略的。省略掉它,是因为反向推理规则都有一个默认的知识库,那就是这个规则库的基本规则类类名所对应的知识库。我们假设这个规则库文件名是myRule.krb,而它是继承自fatherRule.krb,那么这里所缺省的知识库名就是fatherRule。也就是说,如果把这个示例中的use写全,那么应该就是

use fatherRule.child_parent($child, ...)

但pyke规定了,这种情况下必须缺省知识库。然后在when的声明中,每个规则实例是可以指定知识库名也可以不指定。在指定的情况下,其默认知识库名的方法与use相同。