Fibonacci - 斐波纳契数列(一种整数数列, 其中每数等于前面两数之和)
1、Fact 设置
- session.insert( new Fibonacci( 50 ), false );
2、Fibonacci对象定义
- public class Fibonacci {
- private int sequence;
- private long value;
- public Fibonacci() {
- }
- public Fibonacci(final int sequence) {
- this.sequence = sequence;
- this.value = -1;
- }
- public int getSequence() {
- return this.sequence;
- }
- public void setValue(final long value) {
- this.value = value;
- }
- public long getValue() {
- return this.value;
- }
- public String toString() {
- return "Fibonacci(" + this.sequence + "/" + this.value + ")";
- }
- }
3、规则说明
- /*
- 说明:
- 斐波纳契数列(一种整数数列, 其中每数等于前面两数之和)计算
- 本例中最值得思考的是对递归的处理方式,如何开始和结束递归
- 本例中对递归的控制是通过一个sequence整型字段来控制的
- */
- package org.drools.examples
- import org.drools.examples.FibonacciExample.Fibonacci;
- # Recurse规则的实际作用就是不断向Working Memory中插入新的Fibonacci实例,但每次Sequence减1
- # 每个新的Fibonacci实例又再次引起该规则被激活,这样不断循环下去
- # 该规则是当插入第49个Fibonacci实例后,
- # 此实例中sequence等于1时被具有更高优先级的Bootstrap1规则打断
- #
- # 问题1:为何每次都是新插入的Fibonacci激发规则,之前的Fibonacci为什么不会再次匹配
- # 要理解这点,首先要记住Drools的规则激活并不是在Fact插入Working Memory后再去寻找规则
- # 而是在Fact插入时就完成了需要激活规则的判断,激发只是在完成插入后统一激发激活的规则
- # 因此这里每次插入新的Fibonacci同时就再次激活了Recurse规则,直到被Bootstrap1规则打断
- #
- # 问题2:为何要将salience设为10,salience为0也可以被Bootstrap1中止啊?
- # 当插入第48个Fibonacci实例时,此时同时有Recurse和Bootstrap2两个规则被激活
- # 因为Recurse的优先级较高先执行,然后因为插入Fibonacci引起Bootstrap1的执行,接着再回来执行Bootstrap2
- # 规则激活顺序:Recurse -> Bootstrap2 -> Recurse -> Bootstrap1
- # 规则激发顺序:Recurse -> Recurse -> Bootstrap1 -> Bootstrap2
- # 请留意激活与激发的不同,激活相当于规则放入待执行队列
- # 但是具体的执行由冲突执行策略决定,并且可能因为其它规则的执行改变了激活该规则的Fact状态而废除
- #
- # 问题3:如果将Recurse的salience设为0,将发生什么情况?(暂时注释Calculate)
- # 当插入第48个Fibonacci实例时,此时同时有Recurse和Bootstrap2两个规则被激活
- # 而它们的优先级相同,而Drools在一个Working Memory中并不是一个并发的执行引擎,也就是必然有一个规则先激发
- # 假设Recurse先执行,则激发顺序为 Recurse->Bootstrap1->Bootstrap2,另一个Recurse因为Bootstrap1的执行被中止
- # 假设Bootstrap2先执行,则激发顺序为 Bootstrap2,当前的Recurse因为Bootstrap2的执行被中止
- # 这样会有两种不同的执行结果,引擎到底会怎样处理呢?随机发生显然是不合理的。
- # ——秘密就在规则在Drl中的排列顺序,当规则的salience相同,又出现执行顺序冲突时,按照LIFO(后进先出)策略
- # ——排在Drl文件后面的规则插入引擎的顺序在后面,因此执行时先执行,也就是说Bootstrap2会先执行
- # ——如果将Bootstrap2移到Recurse前面,你就会发现是Recurse先执行了
- #
- # 注:Drools引擎对规则执行冲突的解决方案除了上面两种以及外部规则流定义外,是否还存在其它隐含算法,
- # 因为作者没有实际阅读源码不能给出定论,需要按照实际情况多进行研究探索。
- # 要用好规则引擎,除了理解规则的思想外,掌握引擎的执行算法也是非常重要的!
- #
- rule Recurse
- salience 10
- when
- f : Fibonacci ( value == -1 )
- then
- insert( new Fibonacci( f.getSequence() - 1 ) );
- # System.out.println( "recurse for " + f.getSequence() );
- end
- # Bootstrap1规则的作用是结束Recurse规则的递归调用
- # 当第49个Fibonacci实例插入后,同时匹配的有Recurse和Bootstrap1规则
- # Bootstrap1规则因为优先级更高先执行,对激活规则的Fibonacci进行了修改,将Value设为1
- # 因为这个Fibonacci实例也是激活另一个Recurse的Fact,因此当Value被更改后,
- # Recurse的执行条件因为失败而被取消执行
- #
- rule Bootstrap1
- salience 20
- when
- f : Fibonacci( sequence == 1, value == -1 )
- then
- f.setValue( 1 );
- update( f );
- System.out.println( "Bootstrap1:" + f.getSequence() + " == " + f.getValue() );
- end
- # Bootstrap2规则的作用是对倒数第二个插入的Fibonacci实例进行修改
- # 并引发Calculate规则的执行。
- # Bootstrap2规则的激活与激发已经在Recurse中详细讨论过,请参考前面
- # 要特别留意的是因为Bootstrap2的执行,
- # 引擎在对倒数第二个Fibonacci重新评估时发现Calculate规则满足激活条件并激发它
- #
- rule Bootstrap2
- when
- f : Fibonacci( sequence == 2, value == -1 )
- then
- f.setValue( 1 );
- update( f );
- System.out.println( "Bootstrap2:" + f.getSequence() + " == " + f.getValue() );
- end
- # Calculate规则是进行斐波纳契数列计算的核心,之前的规则都是在进行Fact的准备以及控制递归
- # 从规则的激活条件看,需要的是三个sequence值连续的Fibonacci实例
- #
- # 规则执行时,简单的将前面两个Fibonacci的value相加后赋值给第三个Fibonacci
- # 至于retract第一个Fibonacci实例这个操作本身不对执行逻辑有什么影响
- # 在这里使用retract,可能只是为了让逻辑思路更加清晰
- #
- rule Calculate
- when
- f1 : Fibonacci( s1 : sequence, value != -1 )
- f2 : Fibonacci( s2 : sequence == (s1 + 1 ), value != -1 )
- f3 : Fibonacci( sequence == (s2 + 1 ), value == -1 )
- then
- f3.setValue( f1.getValue() + f2.getValue() );
- update( f3 );
- retract( f1 );
- System.out.println( "Calculate:" + f3.getSequence() + " == " + f3.getValue() );
- end