您的位置:首页 > 编程语言

Pyke 逻辑编程入门(10):规则之“正向推理”

2010-03-14 10:23 375 查看

正向推理

规则库激活后,正向推理规则自动启用。
规则库激活后,正向规则执行的顺序,以其在.krb规则库文件中的次序为准。

正向推理的基本情况

为了进行正向推理,Pyke 查看哪个规则的 if 子句,与已知事实相匹配( if 子句可以多次匹配成功,参见“回溯”)。 规则匹配成功后,开始启用它,将其 than 子句中的事实,加入已知事实的列表。
新的事实与其他正向规则 if 子句匹配后,可以将其启用。各种深度的推理过程,都可发生这种匹配。Pyke 把第一个规则的 then 子句,与下一个规则 if 子句,建立链接。

注 意
正向推理的过程,持续到没有规则可供使用。

复习

Pyke 正向推理,从第一个规则的 if 子句开始,查看它是否与已知事实匹配。

如果匹配,就进入 then 子句,启用这个规则。

启用的规则可能与其他规则的 if 子句相关联。

Pyke 此时的推理过程,是从规则的 if 子句进到 then 子句,再从下一规则的 if 子句进到 then 子句。这种方式叫做正向推理。

foreachasssert 代替 ifthen

规则的 if 子句中有事实陈述的模式,它们可能与几个事实匹配,于是,规则可能多次匹配启用。
规则 then 子句的事实陈述也有模式。每当规则启用,then 子句中的模式变量,可以约束成不同值,从而断言出不同事实。
为了避免概念冲突,Pyke 在正向推理过程中使用 foreachassert ,取代 if 和 then。在 if 子句的事实陈述第一列表,逐个匹配整合事实陈述。在规则启用后,断言 then 子句事实陈述第二列表中的事实。

注 意
使用 foreachassert 标志着这是个正向推理规则。

示 例

本例以一组父子关系的事实,揭示某人的父系祖先。为了举例的简明,省略了母亲、女儿的关系。这些事实保存在事实库 family 的知识项 son_of(son, father)。
1  son_of(michael, bruce)
2  son_of(bruce, thomas)
3  son_of(thomas, frederik)
4  son_of(frederik, hiram)

以下列方式传承父子关系:
father_son($father, $son, $prefix)

即:

$son:儿子的名字 (例如 michael)
$father:父亲的名字 (例如 bruce)
$prefix:在“father”和“son”前面的标识,表示是第几代的父子关系 (例如 () 直接的父子关系 father_son,(grand), (祖父 great, 曾祖父 grand), 等等)。
这里用了三条正向推理规则,每条都是单独的运用:

第一步: 直接的父子关系

第一步:展示的是,通过运用模式匹配,把值从规则中的一个事实传递到另一个中。

第二步: 祖父辈的父子关系

第二步:展示的是,在正向推理规则的多个前提条件之间回溯。弄懂它有助于理解回溯。

第三步: 曾祖父辈的父子关系

第二步:展示的是,正向推理规则的递归。

下面,看看怎样运行示例。

第一步: 规则 direct_father_son

首先,需要建立直接的父子关系,即事实 father_son 中的模式变量 $prefix 的值是空元组 ():
1  direct_father_son
2      foreach
3          family.son_of($son, $father)
4      assert
5          family.father_son($father, $son, ())

模式变量的用法

它演示了模式变量,怎样把规则中事实的值,传递到另一个事实。
规则的第2行,是个 foreach 子句,其中只有第3行单独的事实陈述。 这个事实陈述,匹配全部4个 son_of 事实,因此,规则可匹配成功4次,但其中的模式变量 $son 和 $father,约束的值不同。 由此,第5行的 assert 子句运行时,有4次引起断言不同的事实。每次事实的断言,模式变量 $son 和 $father 的值,从第3行传递到第5行。
规则启用,第3行匹配成了:
1  son_of(michael, bruce)

运行到第5行,做出断言:
5  father_son(bruce, michael, ())

规则适用,第3行第二次匹配成:
2  son_of(bruce, thomas)

它又运行到第5行,第二次断言:
6  father_son(thomas, bruce, ())

规则接着对其余二个 son_of 事实,再适用两次,再两次断言 father_son 的关系。于是,这一规则产生了四个新事实:
5  father_son(bruce, michael, ())6  father_son(thomas, bruce, ())7  father_son(frederik, thomas, ())
8 father_son(hiram, frederik, ())

第二步: 规则 grand_father_son

现在我们要加上祖父辈的父子关系。增加新规则:
6  grand_father_son
7      foreach
8          family.father_son($father, $grand_son, ())
9          family.father_son($grand_father, $father, ())
10      assert
11          family.father_son($grand_father, $grand_son, (grand))

回溯的用法

规则 grand_father_son,根据 foreach 子句里事实 father_son 的组合情况(第8第9行),适用运行。同时,根据各次组合的情况,断言一个 father_son 事实(第11行,注意值 (grand) )。
这一规则可以较好地说明回溯,帮助你理解。下面,让我们看看这个规则的回溯运行。
foreach 子句里有两个事实(第8行第9行),期待与第三参数为空元组 () 的事实 father_son 相匹配。
8  family1.father_son($father, $grand_son, ())
9  family1.father_son($grand_father, $father, ())

与之匹配的 family1 事实如下(第5至第8):
5  father_son(bruce, david, ())
6 father_son(thomas, bruce, ())7 father_son(frederik, thomas, ())
8 father_son(hiram, frederik, ())

Pyke 从规则 family1 前提条件列表的顶部,开始为第一个前提(第8行)寻找匹配。匹配合一的是事实5,$father 约束成 bruce。
8  family.father_son($father, $grand_son, ())    => fact 5, SUCCESS
9  family.father_son($grand_father, $father, ())

匹配成功后,Pyke 进到第9行的下个前提。因为 $father 已约束为 bruce,这次成功地与事实6匹配合一。
8  family.father_son($father, $grand_son, ())    => fact 5
9  family.father_son($grand_father, $father, ()) => fact 6, SUCCESS

虽又匹配成功,可 Pyke 已走到前提列表的尽头,于是开始启用规则的断言:
9  father_son(thomas, michael, (grand))

这是个前向推理规则,Pyke 要尽量获得问题的全部答复,所以,它继续运行,仿佛因遇到失败(意思是说,它不喜欢当前的答复)。

注 意
稍后会看到,Pyke 不是以反向推理规则自动运行的。

Pyke 因失败退回到第二个前提条件,寻找以 bruce 为第一参数的其他 father_son 事实。
8  family1.father_son($father, $grand_son, ())    => fact 5
9  family1.father_son($grand_father, $father, ()) => FAILS

失败导致后退,Pyke 退到第一个前提,寻找事实5之外的其他 father_son 事实。结果与事实6匹配,$father 约束成 thomas:
8  family1.father_son($father, $grand_son, ())    => fact 6, SUCCESS
9  family1.father_son($grand_father, $father, ())

成功导致前进,Pyke 进到第二个前提,与事实7匹配:
8  family1.father_son($father, $grand_son, ())    => fact 6
9  family1.father_son($grand_father, $father, ()) => fact 7, SUCCESS

虽又匹配成功,可 Pyke 已走到前提列表的尽头,于是开始启用规则的断言:
10 father_son(frederik, bruce, (grand))

接着,Pyke 败退到第二前提,继续寻找事实7之后的可匹配事实。这失败了。
8  family1.father_son($father, $grand_son, ())    => fact 6
9  family1.father_son($grand_father, $father, ()) => FAILS

失败导致后退,Pyke 退到第一个前提,寻找事实6之后的其他 father_son 事实。因为事实7是最后一个匹配成功的,所以跳过它,直接去试末尾的事实8。结果,与事实8匹配成功,把 $father 约束成 hiram:结果与事实6匹配,$father 约束成 thomas:
8  family1.father_son($father, $grand_son, ())    => fact 8, SUCCESS
9  family1.father_son($grand_father, $father, ())

成功导致前进,Pyke 进到第二个前提,寻找与 hiram 匹配的 father_son for hiram 事实。结果,失败了:
8  family1.father_son($father, $grand_son, ())    => fact 8
9  family1.father_son($grand_father, $father, ()) => FAILS

失败导致后退,Pyke 退到第一个前提,寻找事实8之后的其他可匹配事实。结果没有找到,失败了:
8  family1.father_son($father, $grand_son, ())    => FAILS
9  family1.father_son($grand_father, $father, ())

失败导致后退,但 Pyke 已经退到前提列表的尽头,规则适用失败,Pyke 完成了任务。

重 要
注意,foreach 子句末尾的事实,可以多次匹配成功,由此多次启用 assert 子句。
可是,foreach 子句起始的事实,仅能匹配失败一次。若匹配失败,整个规则适用失败。
因此,适用规则 grand_father_son,结果会再产生3个事实:
9  father_son(thomas, david, (grand))
10 father_son(frederik, bruce, (grand))11 father_son(hiram, thomas, (grand)) (this is the one we skipped)

第三步:规则 great_grand_father_son

最后,我们增加曾祖父辈的父子关系。规则是:
12  great_grand_father_son
13      foreach
14          family1.father_son($father, $gg_son, ())
15          family1.father_son($gg_father, $father, ($prefix1, *$rest_prefixes))
16      assert
17          family1.father_son($gg_father, $gg_son,
(great, $prefix1, *$rest_prefixes))


注 意
注意,第15行事实中的模式变量 $prefixes,如何指定成 ($prefix1, *$rest_prefixes)的形式。结果是,它与空元组 () 不能匹配。 不过,如果 $rest_prefixes 约束成空元组 (),它还可以匹配 (grand)。
这种规则是唯一可以递归适用的。它断言出新事实后,新事实可为相同(与第15行事实匹配的)规则使用,以产生上溯祖祖辈辈的父子关系。

递归的规则

适用这一规则,通常会断言以下事实:
12 father_son(frederik, david, (great, grand))
13 father_son(hiram, bruce, (great, grand))

可是,因为这些事实也可为第15行的相同规则使用,所以,Pyke 会再次运行规则核查新的事实。
尝试匹配第一个新事实: father_son(frederik, david, (great, grand)),但失败了,因为 david 不是父亲。
尝试匹配第二个新事实:father_son(hiram, bruce, (great, grand)) ,结果得到一个新事实:
14 father_son(hiram, david, (great, great, grand))

再次用此规则试配最后的新事实,但因 david 不是父亲而失败。
Pyke 此时完成了这一规则的适用。它启用了三次,断言的事实如下:
12 father_son(frederik, david, (great, grand))
13 father_son(hiram, bruce, (great, grand))14 father_son(hiram, david, (great, great, grand))

运行示例

这些规则可以如下运行:
>>> from pyke import knowledge_engine
>>> engine = knowledge_engine.engine(__file__)
>>> engine.activate('fc_related')     # This is where the rules are run!
>>> engine.get_kb('family1').dump_specific_facts()
father_son('bruce', 'david', ())
father_son('thomas', 'bruce', ())
father_son('frederik', 'thomas', ())
father_son('hiram', 'frederik', ())
father_son('thomas', 'david', ('grand',))
father_son('frederik', 'bruce', ('grand',))
father_son('hiram', 'thomas', ('grand',))
father_son('frederik', 'david', ('great', 'grand'))
father_son('hiram', 'bruce', ('great', 'grand'))
father_son('hiram', 'david', ('great', 'great', 'grand'))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: