顺序算法

与 RetePlus 算法不同,顺序执行方式不提供推论。 仅当规则是同构的,即当它们使用相同的绑定时,顺序算法才以可预测的方式工作。

要保证使用任一算法执行相同的操作,需要满足以下两个条件:

  • 规则必须是同构的。 同构规则表示必须在相同种类和数量的对象上设置这些规则的条件。 如果尝试将顺序算法应用于异构规则,那么它通常会创建更多元组对象,这可能会导致运行的规则比在 RetePlus 方式下运行的规则更多。 常见元组特征符也会导致异构规则不触发: 当工作内存中没有相应的实例时,无法创建任何元组。

  • 不得链接规则,即,在规则的操作部分中进行的修改不得导致另一个规则的执行。

对于可从顺序处理方式获益的规则集,顺序执行方式利用字节码生成,可显着提高规则处理速度。 如果为不符合顺序执行方式的规则指定顺序执行方式,那么引擎会产生错误。

在以下部分中进一步描述了顺序执行方式的使用:

元组

元组是符合特定结构的对象的列表。 元组结构 只是类描述符的序列。 元组结构中的每个位置都称为 。 同一类描述符可以在不同槽中多次出现。

采用元组结构:

(Customer,Product)

以下是符合此结构的元组:

(new Customer("Henry"),new DVD("Mickey"))

请注意,将考虑子类,因为 DVDProduct。 但是,一些不符合结构的元组:

例如,以下元组太大:


(new Customer("Henry"),new CD("Madona"),new DVD("Mickey"))

此元组未正确排序:


(new DVD("Lord of the rings"),new Customer("Henry"))

而这个元组太小了:


(new Customer("Henry"))

在 Java™中,元组可轻松实现为对象数组:


Java Tuple = Object[]

用于创建元组及其对象的 Java 代码为:


new Object[] {new Customer("Henry"),new DVD("Mickey")}

在运行时,将手动或自动从工作内存的内容构建元组,然后将其传递到规则引擎。 元组被一个接一个地传递到元组匹配方法。

仅当元组与元组结构 (由顺序任务中的所有规则共享的公共特征符) 匹配时,才会从工作内存中的对象构造元组。 生成元组后,将针对所有元组运行所有规则。 因此,建议将同构规则与顺序算法配合使用。

如果提供自己的元组,它必须始终与签名匹配,但不一定要完整。 部分 元组是具有未使用的槽的元组。 未使用的槽仅设置为空。 例如,太小的元组(new Customer("Henry")) 可以编码为 (new Customer("Henry"),null) 以实现元组结构。

例如,如果您有以下两个规则,那么特征符为 [Integer , Integer]。 如果工作内存中只有一个整数,那么规则 r2 不会触发。
rule r1 {
when{
  Integer();
  Integer();
}
then {...


rule r2 {
when{
  Integer();
}
then {...
例如,如果您有以下两个规则,那么特征符为 [String , Integer]。 如果工作内存中有两个字符串 (S1, S2) 和一个整数 (I1) ,那么规则 r2 将针对每个元组 [S1触发一次。 I1] [S2, I1]。 这意味着 r2 会针对 I1触发两次。
rule r1 {
when{
  Integer();
  String();
}
then {...


rule r2 {
when{
  Integer();
}
then {...

顺序方式下的规则选择

使用属性 algorithm 等于 sequential 设置的规则任务的规则将动态转换为 Java 字节码。 当任务运行时,字节码能够执行顺序处理。

规则任务的 body 属性是必需的。 它选择规则集中由规则任务考虑的规则。

另请参阅 运行时规则选择 (Runtime rule selection)

顺序方式下的控制属性

控制属性会影响顺序方式执行和元组结构。 有关更多常规信息,请参阅 执行方式中规则任务的控制属性

可以使用以下控制属性指定每个元组要执行的规则数及其执行顺序: 排序触发firinglimit

排序属性

ordering 属性对于顺序处理是必需的。 它描述执行规则任务的规则的顺序。

有两个值可用:

  • literal: 规则按其设置到任务主体中的顺序 (使用向上/向下箭头) 进行保留。

  • sorted: 根据规则的静态优先级对其进行排序。

请注意,另一个值 dynamic仅可用于使用 RetePlus 执行方式的规则任务。

触发属性

firing 属性指定应该对每个元组执行所有规则还是仅执行第一个应用的规则。 此属性是可选的。

它有两个可能的值:

  • allrules

    此值表示应在跳过下一个元组之前执行所有应用的规则。 这是默认值。

  • rule

    此值表示仅应在第一个元组上执行应用的第一个规则。

firinglimit 属性

firing 属性设置为 allrules时, firinglimit 属性将提供另一级别的控制。 它用于指定一个数字,该数字表示跳过下一个元组之前要执行的最大规则数。 此数字应该大于零。

顺序算法的已知限制

在几乎所有情况下,由算法属性等于顺序的单个任务处理的规则数都没有限制。 唯一的例外是具有 otherwise的超大决策表。 处理如下: 所有行的所有条件都在单个表达式中求反。 一旦转换为字节码,此单一表达式将成为单一方法的主体。 这是一个问题,因为 Java 验证器拒绝具有太大主体的方法。 用于规则的可伸缩性算法不会向下到方法主体。 当类装入器拒绝规则时,将向用户通知此限制。 规则数量上的限制是不明确的,但数量很可能是几千个。

下表概述了顺序处理的限制,特别是其与某些 ILOG ® 规则语言 (IRL) 构造结合使用的限制。

限制 描述
ILOG 规则语言 (IRL) 限制

并非为有状态 Rete 匹配设计的所有规则模式都可用。 当规则不符合当前元组匹配实现时,会发生编译时错误。

因此,使用顺序处理时, IRL 不支持以下功能:

  • 动态优先级

    因为顺序处理中没有议程,所以只允许使用具有静态优先级的规则。

  • 没有枚举符的 notexistscollect 条件

    具有枚举符 (fromin) 的 notexistscollect 条件将转换为 Java 字节码。 指定枚举符时,对象集可用作与条件绑定的值。 (请注意,这不是 RetePlus 或 Fastpath 的限制。)

条件构造

以下条件构造在顺序处理中可用:

  • 简单条件-绑定和测试可用

  • from

  • in

异常处理

在顺序和快速路径方式的规则条件部分中不支持异常处理。

例如,如果在上下文上指定异常处理程序,那么在顺序和快速路径方式下不会将其考虑在内:

IlrContext mycontext="....";
IlrExceptionHandler myhandler="....";
mycontext.setExceptionHandler(myhandler);
折射

引擎循环的数据单元是元组。 引擎不会记录元组,因为它们是匹配的,而是在循环之间忘记它们。 因此,没有内置的对折射的支持。

使用顺序执行方式,上述规则任务将变为:


ruletask main {
   algorithm = sequential;
   ordering = literal;
   body = {Person,PersonProduct}
}

计算的元组结构:


(Person,Product)

生成的元组匹配代码:


void matchObjects(Object[] tuple) {
   rule_Person((Person)tuple[0]);
   rule_PersonProduct((Person)tuple[0],(Product)tuple[1]);
}

主要代码保持不变:

  1. context.insert(new Person("Henry"));

  2. context.insert(new CD("Madona"));

  3. context.insert(new DVD("Mickey"));

  4. context.execute();

工作内存迭代器循环:

  1. matcher.matchObjects((Person("Henry"),CD("Madona")));

  2. matcher.matchObjects((Person("Henry"),DVD("Mickey")));

执行跟踪:

  1. // matcher.matchObjects((Person("Henry"),CD("Madona")));

  2. Person(Henry)

  3. PersonProduct(Henry,Madona)

  4. // matcher.matchObjects((Person("Henry"),DVD("Mickey")));

  5. Person(Henry)

  6. PersonProduct(Henry,Mickey)

执行跟踪显示,通过使用顺序执行方式,将对两个不同元组的相同部分运行同一规则两次。

ILOG 规则语言 (IRL) 工具

相比之下,几乎所有 IRL 的脚本级表达式和语句都在顺序处理中可用。 它们主要用于保持未设置为顺序算法的其他规则任务的一致性。

以下是这些功能的列表:

函数

函数定义和调用完全可用。

脚本级别表达式

所有 IRL 表达式都可用。 (请注意, ?instance 在任何执行方式下都有效。)

脚本级语句

所有 IRL 语句都可用。

更新

更新构造可用,因为在设置为顺序算法的规则任务中可能存在虚假更新。 虚假更新是对无法与设置为顺序算法的规则任务的任何条件部分匹配的对象进行的更新,但对于使用 RetePlus 算法的其他规则任务可能很重要。 所有规则任务都在规则集中指定,并且应与当前引擎保持一致。 但是,需要注意的是,通常的更新完全不由无状态顺序处理算法处理。

更新刷新

顺序处理算法根本不会处理刷新方式,但它可能与使用 RetePlus 算法的其他规则任务相关。

插入

从工作内存中抽取对象的元组时,插入可能与设置为顺序算法的规则任务的条件部分相匹配的对象可能会导致不一致。 这是因为对象会插入到第一个位置,而构建元组的迭代器不会收到通知。 因此,迭代器不会将涉及新对象的元组提供给顺序处理引擎。 但是,插入仍与使用 RetePlus 算法的其他规则任务相关。

以下示例显示了导致 Just-In-Time (JIT) 编译错误 (即,执行时的错误而不是解析错误) 的情况。

示例 1 :IRL 限制导致编译错误

此示例由于缺少枚举符而导致 JIT 编译错误。


ruletask T {
  algorithm = sequential;
  body = { R1, R2, R3, R4, R5, R6, R7, R8, R9 }
}

rule R1 {
  when {
    not MyObject();  // No enumerator, an error will occur
  }
  then {
  }
}

rule R2 {
  when {
    exists MyObject();  // No enumerator, an error will occur
  }
  then {
  }
}

rule R3 {
  when {
    collect MyObject() where (size() == 1);  // No enumerator, an error
                                                will occur
  }
  then {
  }
}

function Object getObject() {
  return new MyObject();
}

function Object[] getObjects() {
  Object objs = new Object[1];
  objs[0] = getObject();
  return objs;
}

rule R4 {
  when {
    not MyObject() from getObject();  // An enumerator, no error
  }
  then {
  }
} 

rule R5 {
  when {
    not MyObject() in getObjects();  // An enumerator, no error
  }
  then {
  }
} 

rule R6 {
  when {
    exists MyObject() from getObject();  // An enumerator, no error
  }
  then {
  }
} 

rule R7 {
  when {
    exists MyObject() in getObjects();  // An enumerator, no error
  }
  then {
  }
} 

rule R8 {
  when {
    collect MyObject() from getObject() where (size() == 1); 
    // An enumerator, no error
  }
  then {
  }
} 

rule R9 {
  when {
    collect MyObject() in getObjects() where (size() == 1);  
    // An enumerator, no error
  }
  then {
  }
}

示例 2: 显示构造限制的 IRL

以下是使用 collect 构造来演示绑定作用域的示例。 这也适用于 notexists 。 此示例还会由于使用不受支持的构造而导致 JIT 编译错误。


rule R10 {
  when {
    r:MyRootObject();
    c:collect MyObject(n:name; n.startsWith("ilog")) in r.getObjects(); 
    where (s:size(); s > 1);
  }
  then {
    out.println(r);  // correct
    out.println(c);  // correct
    out.println(s);  // correct
    out.println(n);  // error
  }
}