21.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 的任何规则条件。
红帽决策管理器中的决策引擎使用正向和向链来评估规则。
下图显示了如何使用转发链在逻辑流中的反向链接片段评估规则:
图 21.27. 使用转发和向后链的规则评估逻辑

例如,House of Doom 示例使用包含各种查询类型的规则来查找房间和项目的位置。示例类 Location.java
包含示例中使用的 项目
和位置
元素。示例类 HouseOfDoomMain.java
在其所在位置插入项目或房间,并执行规则。
HouseOfDoomMain.java 类中的项目和位置
ksession.insert( new Location("Office", "House") ); ksession.insert( new Location("Kitchen", "House") ); ksession.insert( new Location("Knife", "Kitchen") ); ksession.insert( new Location("Cheese", "Kitchen") ); ksession.insert( new Location("Desk", "Office") ); ksession.insert( new Location("Chair", "Office") ); ksession.insert( new Location("Computer", "Desk") ); ksession.insert( new Location("Drawer", "Desk") );
示例规则依赖于向后链和递归来确定内部结构中所有项目和房间的位置。
下图展示了 Doom 的 House 的结构以及其中的项目和房间:
图 21.28. Doom 结构内部

要执行示例,请运行 org.drools.examples.backwardchaining.HouseOfDoomMain
类,在 IDE 中作为 Java 应用程序。
执行后,会在 IDE 控制台窗口中显示以下输出:
IDE 控制台中的执行输出
go1 Office is in the House --- go2 Drawer is in the House --- go3 --- Key is in the Office --- go4 Chair is in the Office Desk is in the Office Key is in the Office Computer is in the Office Drawer is in the Office --- go5 Chair is in Office Desk is in Office Drawer is in Desk Key is in Drawer Kitchen is in House Cheese is in Kitchen Knife is in Kitchen Computer is in Desk Office is in House Key is in Office Drawer is in House Computer is in House Key is in House Desk is in House Chair is in House Knife is in House Cheese is in House Computer is in Office Drawer is in Office Key is in Desk
示例中的所有规则均已触发,以检测内部所有项目的位置,并在输出中打印各个项目的位置。
递归查询和相关规则
递归查询会重复搜索数据结构的层次结构,以获得元素之间的关系。
在 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
规则 "go"
打印插入到系统中的每个字符串,以确定如何实施项目,规则 "go1"
调用查询 为ContainedIn
:
规则 "go" 和 "go1"
rule "go" salience 10 when $s : String() then System.out.println( $s ); end rule "go1" when String( this == "go1" ) isContainedIn("Office", "House"; ) then System.out.println( "Office is in the House" ); end
这个示例将 "go1"
字符串插入到决策引擎中,并激活 "go1"
规则来检测该项目 办事处
位于位置 House
中:
插入字符串和触发规则
ksession.insert( "go1" ); ksession.fireAllRules();
IDE 控制台中的规则"go1"输出
go1 Office is in the House
临时防止规则
传输冲突是父元素中包含的元素之间的关系,该元素在分级结构中高于多个级别。
规则 "go2"
标识 Drawer
和 House
的传输冲突:D rawer
在 House
处位于办事处的 Desk
。
rule "go2" when String( this == "go2" ) isContainedIn("Drawer", "House"; ) then System.out.println( "Drawer is in the House" ); end
这个示例将 "go2"
字符串插入到决策引擎中,并激活 "go2"
规则,以检测项目 Drawer
最终在位置 House
中:
插入字符串和触发规则
ksession.insert( "go2" ); ksession.fireAllRules();
IDE 控制台中的规则"go2"输出
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
查询会以递归方式搜索三次,而且在第三个时间,查询检测到Desk
中的Drawer
实例。Location(x==drawer, y==desk)
-
在第一个位置上的此匹配项后,查询会以递归方式搜索结构,以确定
Drawer
位于Desk
中,Desk
位于办事处
,且该办事处
位于House
中。因此,Drawer
位于House
中,该规则会满足。
被动查询规则
被动查询会搜索数据结构的层次结构,以获得元素之间的关系,并在修改结构中的元素时动态更新。
规则 "go3"
函数作为被动查询,通过传输冲突检测到在 办公室中
是否出现新的项目 密钥
: 办公室的
。Drawer
中的密钥
规则"go3"
rule "go3" when String( this == "go3" ) isContainedIn("Key", "Office"; ) then System.out.println( "Key is in the Office" ); end
这个示例将 "go3"
字符串插入到决策引擎中,并激活 "go3"
规则。最初,此规则不满意,因为内部结构中没有项目 密钥
,因此该规则不会产生任何输出。
插入字符串和触发规则
ksession.insert( "go3" ); ksession.fireAllRules();
IDE 控制台中的规则"go3"输出(不满意)
go3
然后,示例在位置 Drawer
中插入一个新项目 密钥
,它位于 办事处
。这个更改满足在 "go3"
规则中传输冲突,输出会被相应地填充。
插入新项目位置和触发规则
ksession.insert( new Location("Key", "Drawer") ); ksession.fireAllRules();
IDE 控制台中的规则"go3"输出(satisfied)
Key is in the Office
此更改也会在查询后续递归搜索中包含的结构中添加另一个级别。
在规则中带有 unbound 参数的查询
带有一个或多个未绑定参数的查询会返回查询定义的(绑定)参数内所有未定义(绑定)项。如果查询中的所有参数都未绑定,则查询会返回查询范围内的所有项目。
规则 "go4"
使用 unbound 参数项搜索绑定参数 办事处
的所有项目,而不是使用 bound 参数搜索 office 中的特定项目:
规则"go4"
rule "go4" when String( this == "go4" ) isContainedIn(thing, "Office"; ) then System.out.println( thing + "is in the Office" ); end
这个示例将 "go4"
字符串插入到决策引擎中,并激活 "go4"
规则,以返回办公室中的所有项目:
插入字符串和触发规则
ksession.insert( "go4" ); ksession.fireAllRules();
IDE 控制台中的规则"go4"输出
go4 Chair is in the Office Desk is in the Office Key is in the Office Computer is in the Office Drawer is in the Office
规则 "go5"
使用 unbound 参数项 和位置
来搜索整个 House
数据结构中的所有项目及其位置:
规则"go5"
rule "go5" when String( this == "go5" ) isContainedIn(thing, location; ) then System.out.println(thing + " is in " + location ); end
这个示例将 "go5"
字符串插入到决策引擎中,并激活 "go5"
规则,以返回 House
数据结构中的所有项目及其位置:
插入字符串和触发规则
ksession.insert( "go5" ); ksession.fireAllRules();
IDE 控制台中的规则"go5"输出
go5 Chair is in Office Desk is in Office Drawer is in Desk Key is in Drawer Kitchen is in House Cheese is in Kitchen Knife is in Kitchen Computer is in Desk Office is in House Key is in Office Drawer is in House Computer is in House Key is in House Desk is in House Chair is in House Knife is in House Cheese is in House Computer is in Office Drawer is in Office Key is in Desk