Droolsでevalが正しく開いた姿勢

4703 ワード

前にdroolsルールの設計とテストをしたとき、LHSのある行にevalの短文がある場合、or短絡は有効ではないという現象が発見された.具体的な状況は以下の例である.
簡単な例を書きました
import java.util.*;

global me.tangliu.drools.Test test;

rule "rule01"
when
    Map(this["A"] == 1000) || eval(test.testBoolean())
then
    System.out.println("reaching then");
end

私が望む場合、A == 1000を実行する必要がなく、RHS文(then部分)に直接ジャンプしますが、実行結果は以下の通りです.
9:51:10.801 [main] DEBUG org.drools.compiler.kie.builder.impl.KieRepositoryImpl - Cannot load a KieRepositoryScanner, using the DummyKieScanner
19:51:11.096 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
going into test boolean
reaching then
19:51:11.112 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
19:51:11.112 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE
19:51:11.112 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED

going into test booleanはtestBooleanメソッドで印刷されたログで、前のtest.testBoolean()が満たされたとき、すぐにorを書いたことを示しています.後の判断は依然として実行され、or短絡は有効ではありません.
これは私が予想していたのとは違いますね.or短絡が発効しない以上、and短絡は?次のように文を変更します.
import java.util.*;

global me.tangliu.drools.Test test;

rule "rule01"
when
    Map(this["A"] == 1000) && eval(test.testBoolean())
then
    System.out.println("reaching then");
end

Aの値を99に変更してルールを走り、testを発見する.testBoolean()は実行されず、and短絡が有効になったことを示します.
実験を繰り返してみると、同じ行に1つ以上のthis["A"] == 1000の判定がある場合、orの関係であれば全てのevalにおける判定が実行され、かつ、1回の判定が実行されるたびに、条件が満たされるとRHSが実行されることが分かった.次のようになります.
rule "rule01"
when
    Map(this["A"] == 1000) || eval(test.testBoolean()) || eval(test.testBoolean2())
then
    System.out.println("reaching then");
end

ルールを走った結果、
going into test boolean
reaching then
reaching then
going into test boolean 2
reaching then

しかし、orリンクではなくandリンクを使用すると、短絡が有効になるのはなぜでしょうか.Googleはいろいろな質問をして、多くない説明を検索することができます.以下のようにします.
The CE eval is essentially a catch-all which allows any semantic code (that returns a primitive boolean) to be executed. This code can refer to variables that were bound in the LHS of the rule, and functions in the rule package. Overuse of eval reduces the declarativeness of your rules and can result in a poorly performing engine. While eval can be used anywhere in the patterns, the best practice is to add it as the last conditional element in the LHS of a rule. Evals cannot be indexed and thus are not as efficient as Field Constraints. However this makes them ideal for being used when functions return values that change over time, which is not allowed within Field Constraints.
val is very convenient as it allows us to include pretty much any condition in a rule. However, it’s considerably slower. With other conditions, Drools can cache (remember) the results because it can figure out when these results need to change. With eval, Drools doesn’t have this visibility. Therefore, all eval expressions need to be rechecked every time the rule is true. If you have to use eval, it’s best to add it as the last condition in a rule—meaning, it will be called less often. If any of the previous conditions return ‘false’, then Drools shortcuts, because there is no need to check any of the remaining conditions.
2つのことを言っただけのように見えます.
  • evalは柔軟で、条件判断
  • にメソッド呼び出しをカスタマイズすることができます.
  • evalは効率が低い(結果をキャッシュできないため)、できるだけ複数の条件の最後の
  • に置く.
    しかし、なぜ短絡文法が有効にならないのかは解けないようです.droolsの文法規則に関するドキュメントをいくつか見て、私の理解は以下の通りです.
    私はdroolsのLHSの意味を理解していません.それは私たちのjavaや他のコードの文法とは異なり、各行は1つの条件(条件間でandリンクを使用する)であり、各条件は複数の制約からなる1つのオブジェクトに対する完全な約束であり、1行に複数のオブジェクトに対する制約判断が存在するべきではありません.すなわち、evalがインラインexpressionではなく条件で使用される場合、1行に1つのevalしかないはずです.具体的には、私たちの文法は次のとおりです.
    when
    	eval( A() || B() )
    then
    

    ではなく
    when 
    	eval( A()) || eval( B())
    then
    

    したがって、最初の例は、次のように書き換えることができます.
    rule "rule01"
    when
        $m: Map()
        eval( Integer.valueOf((String)$m.get("A")) == 1000  || test.testBoolean())
    then
        System.out.println("reaching then");
    end
    

    これにより、A=1000の場合、testBooleanは判断を実行する必要はありません.ただし、コードにタイプ変換を行うのは面倒なので、droolsが自分で変換し、キャッシュを流すことができ、効率を高める可能性があります.
    import function testBoolean(){
    		return me.ltang.drools.Test.testBoolean();
    }
    rule "rule01"
    when
        Map(this["A"] == 1000 || eval(testBoolean()))
    then
        System.out.println("reaching then");
    end
    

    最後に達成された結果は同じであるが,この方法は明らかにより良く,より適切である.