89.10. Doom 示例决策内部(反向连锁和递归)
Doom 示例决策集的 House 展示了决策引擎如何使用向串联和递归来达到等级系统中定义的目标或子项。
以下是 Doom 示例 House 的概述:
-
名称 :
反向链接
-
主类 :
org.drools.examples.backwardchaining.HouseOfDoomMain
(在src/main/java
中) -
模块 :
drools-examples
- 键入: Java 应用程序
-
规则文件 :
org.drools.examples.backwardchaining.BC-Example.drl
(src/main/resources
) - 目标 :演示后向链和递归
反向链规则系统是一个目标驱动的系统,从结论开始,决定引擎尝试满足(通常使用递归)。如果系统无法达到结论或目标,它会搜索部分当前目标的子项。系统会继续这个过程,直到初始的结论是满足或者所有子语满意。
与之相反,正向链规则系统是一个数据驱动的系统,从决策引擎的工作内存中以事实开头,并对该事实的更改做出响应。当对象插入到工作内存时,因为更改是由日程表计划执行而变为 true 的任何规则条件。
Red Hat Process Automation Manager 中的决策引擎使用正向和向后链来评估规则。
下图显示了如何使用转发链在逻辑流中的反向链接片段评估规则:
图 89.27. 使用转发和向后链的规则评估逻辑
例如,House of Doom 示例使用包含各种查询类型的规则来查找房间和项目的位置。示例类 Location.java
包含示例中使用的 项目
和位置
元素。示例类 HouseOfDoomMain.java
在其所在位置插入项目或房间,并执行规则。
HouseOfDoomMain.java 类中的项目和位置
示例规则依赖于向后链和递归来确定内部结构中所有项目和房间的位置。
下图展示了 Doom 的 House 的结构以及其中的项目和房间:
图 89.28. Doom 结构内部
要执行示例,请运行 org.drools.examples.backwardchaining.HouseOfDoomMain
类,在 IDE 中作为 Java 应用程序。
执行后,会在 IDE 控制台窗口中显示以下输出:
IDE 控制台中的执行输出
示例中的所有规则均已触发,以检测内部所有项目的位置,并在输出中打印各个项目的位置。
递归查询和相关规则
递归查询会重复搜索数据结构的层次结构,以获得元素之间的关系。
在 Doom 示例的 House of Doom 示例中,BC-Example.drl
文件包含一个 isContainedIn
查询,示例中大多数规则的查询用于递归评估插入到决策引擎中的数据结构:
BC-Example.drl 中的递归查询
query isContainedIn( String x, String y ) Location( x, y; ) or ( Location( z, y; ) and isContainedIn( x, z; ) ) end
query isContainedIn( String x, String y )
Location( x, y; )
or
( Location( z, y; ) and isContainedIn( x, z; ) )
end
规则 "go"
打印插入到系统中的每个字符串,以确定如何实施项目,规则 "go1"
调用查询 为ContainedIn
:
规则 "go" 和 "go1"
这个示例将 "go1"
字符串插入到决策引擎中,并激活 "go1"
规则来检测该项目 办事处
位于位置 House
中:
插入字符串和触发规则
ksession.insert( "go1" ); ksession.fireAllRules();
ksession.insert( "go1" );
ksession.fireAllRules();
IDE 控制台中的规则"go1"输出
go1 Office is in the House
go1
Office is in the House
临时防止规则
传输冲突是父元素中包含的元素之间的关系,该元素在分级结构中高于多个级别。
规则 "go2"
标识 Drawer
和 House
的传输冲突:D rawer
在 House
处位于办事处的 Desk
。
这个示例将 "go2"
字符串插入到决策引擎中,并激活 "go2"
规则,以检测项目 Drawer
最终在位置 House
中:
插入字符串和触发规则
ksession.insert( "go2" ); ksession.fireAllRules();
ksession.insert( "go2" );
ksession.fireAllRules();
IDE 控制台中的规则"go2"输出
go2 Drawer is in the House
go2
Drawer is in the House
决策引擎根据以下逻辑决定这一结果:
-
查询会以递归方式搜索内部的几个级别,以检测
Drawer
和House
之间传输冲突。 -
查询该选项使用
(z, y;)
而不是使用 Location(xDrawer
不在House
中。 -
z
参数目前未绑定,这意味着它没有值并返回参数中的所有内容。 -
y
参数目前绑定到House
,因此z
返回 office 和Kitchen
。 -
该查询从
办公室
收集信息并递归检查该办事处是否位于办事处
。对于这些参数,调用查询行
isContainedIn(x, z;)
。 -
办事处
没有直接存在Drawer
实例,因此无法找到任何匹配项。 通过
z
unbound,查询会返回办事处
中的数据,并确定 z == Desk。isContainedIn(x==drawer, z==desk)
isContainedIn(x==drawer, z==desk)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow isContainedIn
查询会以递归方式搜索三次,而且在第三个时间,查询检测到Desk
中的Drawer
实例。Location(x==drawer, y==desk)
Location(x==drawer, y==desk)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
在第一个位置上的此匹配项后,查询会以递归方式搜索结构,以确定
Drawer
位于Desk
中,Desk
位于办事处
,且该办事处
位于House
中。因此,Drawer
位于House
中,该规则会满足。
被动查询规则
被动查询会搜索数据结构的层次结构,以获得元素之间的关系,并在修改结构中的元素时动态更新。
规则 "go3"
函数作为被动查询,通过传输冲突检测到在 办公室中
是否出现新的项目 密钥
: 办公室的
。Drawer
中的密钥
规则"go3"
这个示例将 "go3"
字符串插入到决策引擎中,并激活 "go3"
规则。最初,此规则不满意,因为内部结构中没有项目 密钥
,因此该规则不会产生任何输出。
插入字符串和触发规则
ksession.insert( "go3" ); ksession.fireAllRules();
ksession.insert( "go3" );
ksession.fireAllRules();
IDE 控制台中的规则"go3"输出(不满意)
go3
go3
然后,示例在位置 Drawer
中插入一个新项目 密钥
,它位于 办事处
。这个更改满足在 "go3"
规则中传输冲突,输出会被相应地填充。
插入新项目位置和触发规则
ksession.insert( new Location("Key", "Drawer") ); ksession.fireAllRules();
ksession.insert( new Location("Key", "Drawer") );
ksession.fireAllRules();
IDE 控制台中的规则"go3"输出(satisfied)
Key is in the Office
Key is in the Office
此更改也会在查询后续递归搜索中包含的结构中添加另一个级别。
在规则中带有 unbound 参数的查询
带有一个或多个未绑定参数的查询会返回查询定义的(绑定)参数内所有未定义(绑定)项。如果查询中的所有参数都未绑定,则查询会返回查询范围内的所有项目。
规则 "go4"
使用 unbound 参数项搜索绑定参数 办事处
的所有项目,而不是使用 bound 参数搜索 office 中的特定项目:
规则"go4"
这个示例将 "go4"
字符串插入到决策引擎中,并激活 "go4"
规则,以返回办公室中的所有项目:
插入字符串和触发规则
ksession.insert( "go4" ); ksession.fireAllRules();
ksession.insert( "go4" );
ksession.fireAllRules();
IDE 控制台中的规则"go4"输出
规则 "go5"
使用 unbound 参数项 和位置
来搜索整个 House
数据结构中的所有项目及其位置:
规则"go5"
这个示例将 "go5"
字符串插入到决策引擎中,并激活 "go5"
规则,以返回 House
数据结构中的所有项目及其位置:
插入字符串和触发规则
ksession.insert( "go5" ); ksession.fireAllRules();
ksession.insert( "go5" );
ksession.fireAllRules();
IDE 控制台中的规则"go5"输出