第8章 例
Examples ZIP アーカイブファイルをダウンロードしてください。
8.1. HelloWorld の例
| 名前: | HelloWorld の例 |
| メインクラス: | org.drools.examples.helloworld.HelloWorldExample |
| タイプ: | Java アプリケーション |
| 目的: | 簡単な Rules の使用方法を示すチュートリアル |
knowledge bases および sessions をビルドする方法を説明し、監査のロギングとデバッグの出力を実証します (両方とも他の例では省略されています)。
Knowledge Builder を使用して、Drools ルール言語 (DRL) ソースファイルを knowledge base が消費できる複数の Package オブジェクトへ変換します。
add メソッドは、Resource インターフェースと Resource Type の両方をパラメーターとして取ります。Resource インターフェースを使用して、ResourceFactory を介して DRL ソースファイルをクラスパスより読み出します。
注記
Resource を使用して DRL ファイルを URL アドレスなどの他の場所から読み出すことも可能です。必要に応じて複数のファイルを追加できます。
DRL ファイルを追加することも可能です (この場合、Knowledge Builder は各名前空間に対して 1 つのパッケージを作成します)。異なる名前空間を持つ複数のナレッジパッケージを同じ knowledge base に追加することができます。
DRL をすべて追加したら、Knowledge Builder のエラーをチェックします (knowledge base がパッケージを検証しますが、文字列形式のエラー情報にしかアクセスできません。そのため、Knowledge Builder インスタンスよりデバッグを行う必要があります)。
Knowledge Builder コレクションを取得し、KnowledgeBaseFactory より Knowledge Builder をインスタンス化してナレッジパッケージのコレクションを追加します。
例8.1 HelloWorld の例: Knowledge Base および Session の作成
final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
// this will parse and compile in one step
kbuilder.add(ResourceFactory.newClassPathResource
("HelloWorld.drl",HelloWorldExample.class), ResourceType.DRL);
// Check the builder for errors
if (kbuilder.hasErrors())
{
System.out.println(kbuilder.getErrors().toString());
throw new RuntimeException("Unable to compile \"HelloWorld.drl\".");
}
// get the compiled packages (which are serializable)
final Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();
// add the packages to a knowledgebase (deploy the knowledge packages).
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(pkgs);
final StatefulKnowledgeSession ksession =
kbase.newStatefulKnowledgeSession();
JBoss Rules の イベントモデル は、独自の内部プロセスのほとんどを公開します。DebugAgendaEventListener と DebugWorkingMemoryEventListener の 2 つのデフォルトデバッグ listeners が提供されます。これらのリスナーは、デバッグ情報を Error Console へ出力します (listeners をセッションに追加するのは簡単です。この処理については、後で説明します)。
KnowledgeRuntimeLogger は、Agenda と Working Memory listeners から特別に派生されたものです。実行監査 を提供し、この出力はグラフィック表示されます。
重要
logger.close() を呼び出す必要があります。
注記
JBoss Rules の監査ロギング機能を使用し、今後の検査のために実行フローを記録します。
例8.2 HelloWorld の例: イベントのロギングと監査
// setup the debug listeners
ksession.addEventListener( new DebugAgendaEventListener() );
ksession.addEventListener( new DebugWorkingMemoryEventListener() );
// setup the audit logging
KnowledgeRuntimeLogger logger =
KnowledgeRuntimeLoggerFactory.newFileLogger(ksession,"log/helloworld");
HELLO または GOODBYE のどちらかになる status) 単一クラスの単純な例です。
例8.3 HelloWorld の例: メッセージクラス
public static class Message
{
public static final int HELLO = 0;
public static final int GOODBYE = 1;
private String message;
private int status;
...
}
Hello World という言葉が含まれ、HELLO の状態を持つ単一の Message オブジェクトを作成します。作成後、このオブジェクトはエンジンへ挿入され、その時点で fireAllRules() が実行されます。
注記
fireAllRules() メソッド呼び出しに到達するまでに、完全一致し、適切に実行できるルールをエンジンが認識します。
例8.4 実行
final Message message = new Message();
message.setMessage("Hello World");
message.setStatus(Message.HELLO);
ksession.insert(message);
ksession.fireAllRules();
logger.close();
ksession.dispose();
- JBoss Rules IDE で
org.drools.examples.helloworld.HelloWorldExampleクラスを開きます。 - クラスを右クリックし、[] を選択した後、[context menu] から [] を選択します。
注記
fireAllRules() にブレイクポイントを追加し、ksession 変数を選択すると、Hello World がすでにアクティベートされ、Agenda に追加されたことが分かるはずです (これにより、パターン一致の作業がすべて挿入中に実行されたことが確認されます)。

図8.1 fireAllRules Agenda ビュー
System.out へ送られ、debug listener の出力は debug listener へ送られます。
例8.5 コンソールウインドウの System.out
Hello World Goodbye cruel world
例8.6 コンソールウインドウの System.err
==>[ActivationCreated(0): rule=Hello World; tuple=[fid:1:1:org.drools.examples.HelloWorldExample$Message@17cec96]] [ObjectInserted: handle=[fid:1:1:org.drools.examples.HelloWorldExample$Message@17cec96]; object=org.drools.examples.HelloWorldExample$Message@17cec96] [BeforeActivationFired: rule=Hello World; tuple=[fid:1:1:org.drools.examples.HelloWorldExample$Message@17cec96]] ==>[ActivationCreated(4): rule=Good Bye; tuple=[fid:1:2:org.drools.examples.HelloWorldExample$Message@17cec96]] [ObjectUpdated: handle=[fid:1:2:org.drools.examples.HelloWorldExample$Message@17cec96]; old_object=org.drools.examples.HelloWorldExample$Message@17cec96; new_object=org.drools.examples.HelloWorldExample$Message@17cec96] [AfterActivationFired(0): rule=Hello World] [BeforeActivationFired: rule=Good Bye; tuple=[fid:1:2:org.drools.examples.HelloWorldExample$Message@17cec96]] [AfterActivationFired(4): rule=Good Bye]
when 以降) は、working memory への挿入時に、Message.HELLO 状態の各 Message オブジェクトに対してアクティベートされることを示しています。
message (message 属性へバインドされる) と m (一致した Message オブジェクト自体にバインドされる) の 2 つの変数バインディングが作成されるよう指示します。
then 以降) はルールの「結果」部分です。ルールの dialect 属性で宣言された通り、MVEL で書かれていることを確認してください。ルールのこの部分は、bound variable message の内容を System.out へ送信します。この後、MVEL の modify ステートメントより、 m にバインドされた message object に含まれる message および status 属性を変更します。このステートメントにより、割り当てのブロックを一度に適用できます (ブロックが完了すると、エンジンは自動的に変更を通知されます)。
例8.7 Hello World のルール
rule "Hello World"
dialect "mvel"
when
m : Message( status == Message.HELLO, message : message )
then
System.out.println( message );
modify (m) { message="Goodbye cruel world", status=Message.GOODBYE };
end
DRLファイルのmodify呼び出し上にブレイクポイントを設定します。- JBoss Rules IDE の
org.drools.examples.HelloWorldクラスを開きます。 - コンテキストメニューへ移動し、[ ] をクリックした後に [] を選択し、実行を開始します。
Good Bye という別のルールは Java を使用します。このルールはここでアクティベートされ、アジェンダに置かれます。

図8.2 Hello World ルールの Agenda ビュー
Good Bye ルールは Hello World ルールと似ていますが、Message.GOODBYE 状態の Message オブジェクトと一致します。
例8.8 Good Bye ルール
rule "Good Bye" dialect "java" when Message( status == Message.GOODBYE, message : message ) then System.out.println( message ); end
KnowledgeRuntimeLoggerFactory メソッドの newFileLogger を使用して KnowledgeRuntimeLogger を作成した Java コードをもう一度思い出してください。このコードは最後に logger.close() を呼び出しました。このようにして、Audit に表示される監査ログファイルが作成されました。
注記
- オブジェクトが挿入され、
Hello Worldルールのアクティベーションが作成されます。 - アクティベーションが実行され、
Messageオブジェクトが更新されます。その結果、Good Byeルールがアクティベートされ、実行されます。 - Audit ビューでイベントを選択すると、元のイベントが緑色で強調表示されます (この例では、
Activation executedイベントの元となるActivation createdイベントが緑色で強調表示されます)。

図8.3 Audit ビュー
8.2. 状態の例
| 名前: | 状態の例 |
| 状態クラス: | org.drools.examples.state.StateExampleUsingSalience |
| タイプ: | Java アプリケーション |
| Rules ファイル: | StateExampleUsingSalience.drl |
| 目的: | 基本的なルールの使用とルール実行優先度における競合の解決方法を実証します。 |
working memory のファクトへの変更に基づいてルールを順に評価、アクティベート、および実行する engine の機能のことです。
8.2.1. 状態の例について
org.drools.examples.state.State クラスには、名前と現在の状態の 2 つのフィールドがあります。現在の状態は以下のいずれかになります。
NOTRUNFINISHED
例8.9 状態クラス
public class State {
public static final int NOTRUN = 0;
public static final int FINISHED = 1;
private final PropertyChangeSupport changes =
new PropertyChangeSupport( this );
private String name;
private int state;
... setters and getters go here...
}
org.drools.examples.state.State を無視すると (理由は後で説明します)、A、 B、 C、および D という名前の 4 つの State オブジェクトが作成されたことが分かります。当初これらの状態は、使用されるコンストラクターのデフォルト値である NOTRUN に設定されます。各インスタンスが順に session にアサートされ、fireAllRules() が呼び出されます。
例8.10 Salience 状態の実行例
State a = new State( "A" );
State b = new State( "B" );
State c = new State( "C" );
final State d = new State( "D" );
// By setting dynamic to TRUE, JBoss Rules will use JavaBean
// PropertyChangeListeners so you do not have to call modify or update().
boolean dynamic = true;
session.insert( a, dynamic );
session.insert( b, dynamic );
session.insert( c, dynamic );
session.insert( d, dynamic );
session.fireAllRules();
session.dispose(); // Stateful rule session must always be disposed when finished</programlisting>
- JBoss Rules IDE で
org.drools.examples.state.StateExampleUsingSalienceクラスを開きます。 - class を右クリックし、[] を選択した後、[] を選択します。
例8.11 Salience 状態のコンソール出力
A finished B finished C finished D finished
Bootstrap ルールが最初に実行され、A が FINISHED 状態に設定されると、B の状態も FINISHED になります。(C と D は両方とも B に依存するため、一時的に競合が発生しますが、salience 値によって解決されます)。
- Audit View が表示されていない場合、[] をクリックし、[]、[]、[]、[] と順番に選択します。
- Audit View で Open Log ボタンをクリックし、
drools-examples-drl-dir>/log/state.logというファイルを選択します。

図8.4 Salience 状態の Audit View 例
working memory に書き込まれた変更が記録されているのが分かります。そのため、状態が NOTRUN の State オブジェクト A をアサートすると、Bootstrap ルールがアクティベートされますが、他の State オブジェクトをアサートしてもすぐに影響はありません。
例8.12 Salience 状態: Bootstrap ルール
rule Bootstrap when a : State(name == "A", state == State.NOTRUN ) then System.out.println(a.getName() + " finished" ); a.setState( State.FINISHED ); end
Bootstrap ルールを実行すると、A の状態が FINISHED に変わり、A to B ルールがアクティベートされます。
例8.13 A to B ルール
rule "A to B" when State(name == "A", state == State.FINISHED ) b : State(name == "B", state == State.NOTRUN ) then System.out.println(b.getName() + " finished" ); b.setState( State.FINISHED ); end
A to B ルールを実行すると B の状態が FINISHED に変わり、B to C ルールと B to D ルールの両方がアクティベートされ、アクティベーションが agenda に置かれます。
engine の agenda はどちらのルールを実行するか決定することができます。B to C ルールの salience 値の方が大きいため (10、デフォルト値は 0) 最初に実行され、オブジェクト C が FINISHED 状態に設定されます。
A to B ルールの State オブジェクトへの変更を表しています。Audit View が開かれている間にルール内にデバッグポイントを配置することができるため、Agenda View を使用してアジェンダの状態を調査することも可能です。以下のスクリーンショットは、A to B ルールのブレークポイントを表しています。また、2 つのルールが競合状態にある時の agenda の状態も表しています。

図8.5 状態の例: Agenda View
例8.14 B to C ルール
rule "B to C" salience 10 when State(name == "B", state == State.FINISHED ) c : State(name == "C", state == State.NOTRUN ) then System.out.println(c.getName() + " finished" ); c.setState( State.FINISHED ); end
B to D ルールは最後に実行され、オブジェクト D の状態を FINISHED に変更します。
例8.15 B to D ルール
rule "B to D" when State(name == "B", state == State.FINISHED ) d : State(name == "D", state == State.NOTRUN ) then System.out.println(d.getName() + " finished" ); d.setState( State.FINISHED ); end
engine が停止します。
PropertyChangeListener オブジェクトに基づいた 動的ファクト です。エンジンが fact プロパティーの変更を「確認」し、反応するには、アプリケーションがこれらの情報をエンジンに通知する必要があります。これは、ルール (modify ステートメント) を介して明示的に行うか、暗黙的 (ファクトが実装された PropertyChangeSupport よりエンジンに通知) に行うことが可能です。
注記
PropertyChangeSupport は Java Beans Specification に定義されます。
PropertyChangeSupport を使用する方法について学びましょう (これを使用すると、ルールが modify ステートメントだらけにならないようにすることが可能です)。この機能を使用するには、org.drools.example.State クラスと同じ方法で、最初にファクトが PropertyChangeSupport を実装するようにします。その後、次のコードを使用してファクトを working memory に挿入します。
例8.16 動的ファクトの挿入
// By setting dynamic to TRUE, JBoss Rules will use JavaBean // PropertyChangeListeners so that one does not have to call modify or update(). final boolean dynamic = true; session.insert( fact, dynamic );
PropertyChangeListener オブジェクトが使用される場合、各 セッター は追加のコード (通知のため) を実装する必要があります。org.drools.examples クラスの State のセッターは次の通りです。
例8.17 「セッター」の PropertyChangeListener サポート
public void setState(final int newState) {
int oldState = this.state;
this.state = newState;
this.changes.firePropertyChange( "state",oldState,newState );
}
StateExampleUsingAgendGroup と StateExampleWithDynamicRules と呼ばれます。上記の通り、これらのクラスは A to B から C to D へ実行されます。
StateExampleUsingAgendGroupクラスはagenda groupsクラスを使用してルールの競合を制御し、最初に実行するルールを決定します。StateExampleWithDynamicRulesクラスは、実行しているworking memoryセッションへルールを追加する方法を表します。
MAIN というアジェンダグループに含まれます。agenda-group 属性により、ルールの異なる agenda group を指定することができます。最初に、MAIN agenda group は working memory によって使用されます。
重要
setFocus() メソッドまたは auto-focus ルール属性を使用します (auto-focus ルール属性を使用する場合、ルールが一致し、アクティベートされると自動的にフォーカスが agenda group に設定されるため、auto-focus という名前になっています。このメソッドは、B to D ルールの前に B to C ルールを実行できるようにします)。
例8.18 アジェンダグループの状態の例: B to C ルール
rule "B to C"
agenda-group "B to C"
auto-focus true
when
State(name == "B", state == State.FINISHED )
c : State(name == "C", state == State.NOTRUN )
then
System.out.println(c.getName() + " finished" );
c.setState( State.FINISHED );
kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "B to D" ).setFocus();
end
B to C は B to D agenda group 上で setFocus() を呼び出します。これにより、アクティブなルールを実行でき、B to D に属するルールをトリガーします。
例8.19 アジェンダグループの状態の例: B to D ルール
rule "B to D" agenda-group "B to D" when State(name == "B", state == State.FINISHED ) d : State(name == "D", state == State.NOTRUN ) then System.out.println(d.getName() + " finished" ); d.setState( State.FINISHED ); end
StateExampleWithDynamicRules の例は、fireAllRules() の実行後、他のルールをベースに追加します。この新しいルールは他の 状態遷移 です。
例8.20 動的状態の例: D to E ルール
rule "D to E" when State(name == "D", state == State.FINISHED ) e : State(name == "E", state == State.NOTRUN ) then System.out.println(e.getName() + " finished" ); e.setState( State.FINISHED ); end
例8.21 動的状態の出力例
A finished B finished C finished D finished E finished
8.3. フィボナッチの例
| 名前: | フィボナッチ |
| メインクラス: | org.drools.examples.fibonacci.FibonacciExample |
| タイプ: | Java アプリケーション |
| Rules ファイル: | Fibonacci.drl |
| 目的: | 再帰およびクロス積の一致を実証します。 |
Fibonacci 単一ファクトクラスを使用します。このクラスには sequence と value の 2 つのフィールドがあります。sequence フィールドは、フィボナッチ数列のオブジェクトの位置を示すために使用されます。value フィールドは、特定の数列位置に対する Fibonacci オブジェクトの値を示します (算出する必要がある値を示すために -1 を使用)。
例8.22 フィボナッチクラス
public static class Fibonacci {
private int sequence;
private long value;
public Fibonacci( final int sequence ) {
this.sequence = sequence;
this.value = -1;
}
... setters and getters go here...
}
- JBoss Rules の統合開発環境で
org.drools.examples.FibonacciExampleクラスを開きます。 - このクラスを右クリックし、 [] を選択した後に [] を選択します。
...snip... は行が省略されていることを示しています)。
例8.23 フィボナッチの例: コンソールの出力
recurse for 50 recurse for 49 recurse for 48 recurse for 47 ...snip... recurse for 5 recurse for 4 recurse for 3 recurse for 2 1 == 1 2 == 1 3 == 2 4 == 3 5 == 5 6 == 8 ...snip... 47 == 2971215073 48 == 4807526976 49 == 7778742049 50 == 12586269025
fifty を持つ単一の Fibonacci オブジェクトを挿入します。その後、再帰ルールが実行され、他の 49 個のオブジェクトが自動的に挿入されます。
注記
PropertyChangeSupport を使用しません。この代わりに、MVFLEX 式言語を使用するため、modify キーワードを使用できます。このキーワードにより、ブロックセッターアクション を使用できます (エンジンに変更も通知します)。
例8.24 フィボナッチの例: 実行
ksession.insert( new Fibonacci( 50 ) ); ksession.fireAllRules();
Recurse ルールは非常に単純です。このルールは、-1 を値として持つアサートされた Fibonacci オブジェクトを照合するため、現在よりも 1 つ前の値を持つ新しい Fibonacci オブジェクトが作成され、アサートされます。数列フィールドが 1 である Fibonacci オブジェクトが存在しない限り、Fibonacci オブジェクトが追加されるたびにルールが実行されます。
Bootstrap ルールを実行する前に 50 個の Fibonacci オブジェクトをすべてアサートする必要があるため、ルールは salience 値も持っています。
例8.25 フィボナッチの例:「再帰」ルール
rule Recurse salience 10 when f : Fibonacci ( value == -1 ) not ( Fibonacci ( sequence == 1 ) ) then insert( new Fibonacci( f.sequence - 1 ) ); System.out.println( "recurse for " + f.sequence ); end
50) を表示します。その後、アサートされた各 Fibonacci オブジェクトによって Recurse ルールが何度も実行される、ルールの連続的な再帰を表します。

図8.6 フィボナッチの例: Recurse Audit ビュー 1
2 である Fibonacci オブジェクトがアサートされると、Bootstrap ルールと一致し、Bootstrap ルールと共にアクティベートされます。
注記
Bootstrap フィールドには複数の制限があります。これらの制限は、フィールドの値が 1 または 2 と等しいか確認します。
例8.26 フィボナッチの例: Bootstrap ルール
rule Bootstrap
when
f : Fibonacci( sequence == 1 || == 2, value == -1 )
// this is a multi-restriction || on a single field
then
modify ( f ){ value = 1 };
System.out.println( f.sequence + " == " + f.value );
endRecurse ルールの salience 値の方が大きいため、Bootstrap ルールは実行されません。

図8.7 フィボナッチの例: Recurse Agenda ビュー 1
1 の Fibonacci オブジェクトがアサートされると、再度 Recurse ルールに対して一致し、2 度アクティベートされます。
注記
1 である Fibonacci オブジェクトが存在すると即座にルールの一致が行われないようになるため、Recurse は一致せず、アクティベートされません。

図8.8 フィボナッチの例: Recurse Agenda ビュー 2
-1 と等しくない値を持つ 2 つのオブジェクトが存在すると、Calculate ルールが実行されます (Bootstrap ルールがオブジェクトに 1 と 2 から 1 の数列値を設定したことに注意してください)。
working memory に 50 個の Fibonacci オブジェクトが存在します。適切な「3 つ組」を選択して順に値を計算します。
Calculate ルールはフィールド制約を使用して、3 つのフィボナッチパターンを正しい順番に制限します。この方法は クロス積の一致 と呼ばれます。この仕組みは次の通りです。
- 最初のパターンは、値が
!= -1の Fibonacci オブジェクトを見つけ、パターンとフィールドの両方をバインドします。 - 2 つ目の Fibonacci オブジェクトも同じ事を行いますが、別のフィールド制約を追加します。これは、f1 にバインドされた Fibonacci オブジェクトよりも数列が大きくなるようにするためです。このルールが最初に実行される時、最初と 2 番目の数列のみ値が
1になります。2 つの制約は、 f1 が最初の数列を参照し、f2が 2 番目の数列を参照するようにします。 - 最後のパターンは、
-1と等しい値を持ち、f2に含まれる値よりも 1 大きい数列値を持つ Fibonacci オブジェクトを見つけます。
f3 へバインドされる) を計算できます。
例8.27 フィボナッチの例: Calculate ルール
rule Calculate
when
// Bind f1 and s1
f1 : Fibonacci( s1 : sequence, value != -1 )
// Bind f2 and v2; refer to bound variable s1
f2 : Fibonacci( sequence == (s1 + 1), v2 : value != -1 )
// Bind f3 and s3; alternative reference of f2.sequence
f3 : Fibonacci( s3 : sequence == (f2.sequence + 1 ), value == -1 )
then
// Note the various referencing rechniques.
modify ( f3 ) { value = f1.value + v2 };
System.out.println( s3 + " == " + f3.value );
end
Modify ステートメントは f3 にバインドされるオブジェクトの値を更新します。その結果、値が -1 と等しくない別の新しいオブジェクトが存在することになります。このオブジェクトが作成されると、Calculate ルールが再度一致します。その後、次のフィボナッチ数を処理します。次図の Audit ビューはこの処理の概要を表しています。これは、最後に実行される Bootstrap ルールがどのように Fibonacci オブジェクトを変更し、Calculate ルールをトリガーするかを表しています。その後、別の Fibonacci オブジェクトが変更され、Calculate ルールが再実行されます。このサイクルは、オブジェクトすべてに値が設定されるまで継続されます。

図8.9 フィボナッチの例: Bootstrap Audit ビュー
8.4. 銀行取引のチュートリアル
| 名前: | 銀行取引のチュートリアル |
| メインクラス: | org.drools.tutorials.banking.BankingExamplesApp.java |
| タイプ: | Java アプリケーション |
| Rules ファイル: | org.drools.tutorials.banking.*.drl |
| 目的: | パターンの一致、基本的なソート、および計算ルールを実証します。 |
重要
RuleRunner クラスは、データのセットに対して 1 つまたは複数の DRL ファイルを実行して、ナレッジパッケージをコンパイルし、各実行に対して knowledge base を作成します。これは、テストおよびチュートリアルの目的で使用できますが、knowledge base を一度だけ構築してキャッシュする必要がある実稼働システムでは適切でないことに注意してください。
例8.28 銀行取引のチュートリアル: RuleRunner
public class RuleRunner {
public RuleRunner() {}
public void runRules(String[] rules,Object[] facts) throws Exception
{
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
KnowledgeBuilder kbuilder =
KnowledgeBuilderFactory.newKnowledgeBuilder();
for ( int i = 0; i < rules.length; i++ ) {
String ruleFile = rules[i];
System.out.println( "Loading file: " + ruleFile );
kbuilder.add( ResourceFactory.newClassPathResource
( ruleFile, RuleRunner.class ),ResourceType.DRL );
}
Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();
kbase.addKnowledgePackages( pkgs );
StatefulKnowledgeSession ksession =
kbase.newStatefulKnowledgeSession();
for ( int i = 0; i < facts.length; i++ ) {
Object fact = facts[i];
System.out.println( "Inserting fact: " + fact );
ksession.insert( fact );
}
ksession.fireAllRules();
}
}Example.drl という単一の DRL ファイルをロードし、実行しますが、データは挿入しません。
例8.29 銀行取引のチュートリアル: Java の例 1
public class Example1
{
public static void main(String[] args) throws Exception
{
new RuleRunner().runRules(new String[]{"Example1.drl"},
new Object[0] );
}
}
true になります。そのため、常に一致し、起動後に一度「実行」します。
例8.30 銀行取引のチュートリアル: Example1.drl のルール
rule "Rule 01" when eval( 1==1 ) then System.out.println( "Rule 01 Works" ); end
print ステートメントに一致し、実行することを表しています。
例8.31 銀行取引のチュートリアル: Example1.java の出力
Loading file: Example1.drl Rule 01 Works
例8.32 銀行取引のチュートリアル: Java の例 2
public class Example2
{
private static Integer wrap(int i) {return new Integer(i);}
public static void main(String[] args) throws Exception
{
Number[] numbers = new Number[] {wrap(3), wrap(1), wrap(4),
wrap(1), wrap(5)};
new RuleRunner().runRules(new String[] { "Example2.drl" },numbers);
}
}
java.lang.Integer クラスのセットを使用します (コレクション番号が「fact」でも「thing 」でもないため、最良の方法ではありません。銀行の口座には残高を表す数字があります。そのため、この場合、口座が「fact」になります。しかし、最初の実証の目的を満たすには整数をアサートすれば十分です)。
例8.33 銀行取引のチュートリアル: Example2.drl のルール
rule "Rule 02"
when
Number( $intValue : intValue )
then
System.out.println("Number found with value: " + $intValue);
end
例8.34 銀行取引のチュートリアル: Example2.drl の出力
Loading file: Example2.drl Inserting fact: 3 Inserting fact: 1 Inserting fact: 4 Inserting fact: 1 Inserting fact: 5 Number found with value: 5 Number found with value: 1 Number found with value: 4 Number found with value: 1 Number found with value: 3
Cashflow クラスを日付順に適用するタスクがあるため、ここでプロセスについて説明します。
例8.35 銀行取引のチュートリアル: Example3.java
public class Example3
{
private static Integer wrap(int i) {return new Integer(i);}
public static void main(String[] args) throws Exception
{
Number[] numbers = new Number[] {wrap(3), wrap(1), wrap(4),
wrap(1), wrap(5)};
new RuleRunner().runRules(new String[]{ "Example3.drl"},numbers);
}
}
例8.36 銀行取引のチュートリアル: Example3.drl のルール
rule "Rule 03"
when
$number : Number( )
not Number( intValue < $number.intValue )
then
System.out.println("Number found with value: "+$number.intValue() );
retract( $number );
end
例8.37 銀行取引のチュートリアル: Example3.java の出力
Loading file: Example3.drl Inserting fact: 3 Inserting fact: 1 Inserting fact: 4 Inserting fact: 1 Inserting fact: 5 Number found with value: 1 Number found with value: 1 Number found with value: 3 Number found with value: 4 Number found with value: 5
Cashflow クラスオブジェクトを作成します。
例8.38 銀行取引のチュートリアル: Cashflow クラス
public class Cashflow
{
private Date date;
private double amount;
public Cashflow() {}
public Cashflow(Date date,double amount)
{
this.date = date;this.amount = amount;
}
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date; }
public double getAmount() { return amount; }
public void setAmount(double amount) { this.amount = amount; }
public String toString()
{
return "Cashflow[date=" + date + ",amount=" + amount + "]";
}
}
Cashflow クラスには、日付と金額の 2 つの簡単な属性があります。toString メソッドが追加されているため、出力することができます (浮動小数点形式は多くの数型を正確に表せないため、通貨単位に double 型を使用することは通常推奨されません)。
Cashflow オブジェクトを挿入します。
例8.39 銀行取引のチュートリアル: Example4.java
public class Example4
{
public static void main(String[] args) throws Exception
{
Object[] cashflows = {
new Cashflow(new SimpleDate("01/01/2007"), 300.00),
new Cashflow(new SimpleDate("05/01/2007"), 100.00),
new Cashflow(new SimpleDate("11/01/2007"), 500.00),
new Cashflow(new SimpleDate("07/01/2007"), 800.00),
new Cashflow(new SimpleDate("02/01/2007"), 400.00),
};
new RuleRunner().runRules(new String[] {"Example4.drl"},cashflows;
}
}
SimpleDate 「コンビニエンス」クラスは、入力文字列を取り日付形式を定義するコンストラクターを提供し、java.util.Date クラスを拡張します。コードは次の通りです。
例8.40 銀行取引のチュートリアル: クラス SimpleDate
public class SimpleDate extends Date
{
private static final SimpleDateFormat format =
new SimpleDateFormat("dd/MM/yyyy");
public SimpleDate(String datestr) throws Exception
{
setTime(format.parse(datestr).getTime());
}
}
cashflow がどのように出力されるかを学ぶため、cashflow ファイルを検証します。
例8.41 銀行取引のチュートリアル: Example4.drl のルール
rule "Rule 04"
when
$cashflow : Cashflow( $date : date, $amount : amount )
not Cashflow( date < $date)
then
System.out.println("Cashflow: "+$date+" :: "+$amount);
retract($cashflow);
end
cashflow を識別し、日付と金額を抽出できます。ルールの 2 行目では、見つかった cashflow よりも日付が古い cashflow が存在しないようにします。結果として、ルールを満たす cashflow を出力して取り消し、日付が次に新しい cashflow を処理できるようにします。
例8.42 銀行取引のチュートリアル: Example4.java の出力
Loading file: Example4.drl Inserting fact: Cashflow[date=Mon Jan 01 00:00:00 GMT 2007,amount=300.0] Inserting fact: Cashflow[date=Fri Jan 05 00:00:00 GMT 2007,amount=100.0] Inserting fact: Cashflow[date=Thu Jan 11 00:00:00 GMT 2007,amount=500.0] Inserting fact: Cashflow[date=Sun Jan 07 00:00:00 GMT 2007,amount=800.0] Inserting fact: Cashflow[date=Tue Jan 02 00:00:00 GMT 2007,amount=400.0] Cashflow: Mon Jan 01 00:00:00 GMT 2007 :: 300.0 Cashflow: Tue Jan 02 00:00:00 GMT 2007 :: 400.0 Cashflow: Fri Jan 05 00:00:00 GMT 2007 :: 100.0 Cashflow: Sun Jan 07 00:00:00 GMT 2007 :: 800.0 Cashflow: Thu Jan 11 00:00:00 GMT 2007 :: 500.0
TypedCashflow を作成するため Cashflow クラスを拡張します (入金または出金操作のいずれかになります)。通常、Cashflow に追加するのが最も簡単な方法ですが、ここでは以前のバージョンのクラスを保持するために拡張が使用されます。
public class TypedCashflow extends Cashflow {
public static final int CREDIT = 0;
public static final int DEBIT = 1;
private int type;
public TypedCashflow() { }
public TypedCashflow(Date date, int type, double amount)
{
super( date, amount );
this.type = type;
}
public int getType()
{
return type;
}
public void setType(int type)
{
this.type = type;
}
public String toString()
{
return "TypedCashflow[date=" + getDate()
+ ",type=" + (type == CREDIT ? "Credit" : "Debit")
+ ",amount=" + getAmount()
+ "]";
}
}
例8.43 銀行取引のチュートリアル: Example5.java
public class Example5
{
public static void main(String[] args) throws Exception
{
Object[] cashflows = {
new TypedCashflow(new SimpleDate("01/01/2007"),
TypedCashflow.CREDIT, 300.00),
new TypedCashflow(new SimpleDate("05/01/2007"),
TypedCashflow.CREDIT, 100.00),
new TypedCashflow(new SimpleDate("11/01/2007"),
TypedCashflow.CREDIT, 500.00),
new TypedCashflow(new SimpleDate("07/01/2007"),
TypedCashflow.DEBIT, 800.00),
new TypedCashflow(new SimpleDate("02/01/2007"),
TypedCashflow.DEBIT, 400.00),
};
new RuleRunner().runRules(
new String[] { "Example5.drl" }, cashflows );
}
}
Cashflow オブジェクトのセットが作成され、各オブジェクトは入金または出金操作のいずれかになります。これらのオブジェクトは Example5.drl とともに RuleEngine へ提供されます。
Cashflow オブジェクトを出力します。
例8.44 銀行取引のチュートリアル: Example5.drl のルール
rule "Rule 05"
when
$cashflow : TypedCashflow( $date : date, $amount : amount,
type == TypedCashflow.CREDIT )
not TypedCashflow( date < $date, type == TypedCashflow.CREDIT )
then
System.out.println("Credit: "+$date+" :: "+$amount);
retract($cashflow);
end
Cashflow ファクトを識別し、日付と金額を抽出することができます。ルールの 2 行目では、見つかった Cashflow よりも日付が古い同じ型の Cashflow が存在しないようにします。結果として、ルールを満たす Cashflow を出力して取り消し、日付が次に新しい CREDIT 型の Cashflow を処理できるようにします。
例8.45 銀行取引のチュートリアル: Example5.java の出力
Loading file: Example5.drl Inserting fact: TypedCashflow[date=Mon Jan 01 00:00:00 GMT 2007,type=Credit,amount=300.0] Inserting fact: TypedCashflow[date=Fri Jan 05 00:00:00 GMT 2007,type=Credit,amount=100.0] Inserting fact: TypedCashflow[date=Thu Jan 11 00:00:00 GMT 2007,type=Credit,amount=500.0] Inserting fact: TypedCashflow[date=Sun Jan 07 00:00:00 GMT 2007,type=Debit,amount=800.0] Inserting fact: TypedCashflow[date=Tue Jan 02 00:00:00 GMT 2007,type=Debit,amount=400.0] Credit: Mon Jan 01 00:00:00 GMT 2007 :: 300.0 Credit: Fri Jan 05 00:00:00 GMT 2007 :: 100.0 Credit: Thu Jan 11 00:00:00 GMT 2007 :: 500.0
Account オブジェクトを 2 つ作成し、RuleEngine へ渡す前に Cashflow クラスへインジェクトします (helper を用いずに正しい口座へ簡単にアクセスできるようにするため、この作業を行います)。
Account クラスを見てみましょう。これは単純な Java オブジェクトで、口座番号と残高の両方を持ちます。
public class Account
{
private long accountNo;
private double balance = 0;
public Account() { }
public Account(long accountNo)
{
this.accountNo = accountNo;
}
public long getAccountNo()
{
return accountNo;
}
public void setAccountNo(long accountNo)
{
this.accountNo = accountNo;
}
public double getBalance()
{
return balance;
}
public void setBalance(double balance)
{
this.balance = balance;
}
public String toString()
{
return "Account[" + "accountNo=" + accountNo
+ ",balance=" + balance + "]";
}
}
AllocatedCashflow クラスを作成するため TypedCashflow を拡張し、Account 参照が含まれるようにします。
例8.46 AllocatedCashflow クラス
public class AllocatedCashflow extends TypedCashflow
{
private Account account;
public AllocatedCashflow() {}
public AllocatedCashflow(Account account, Date date,
int type, double amount)
{
super( date, type, amount );
this.account = account;
}
public Account getAccount()
{
return account;
}
public void setAccount(Account account)
{
this.account = account;
}
public String toString()
{
return "AllocatedCashflow["
+ "account=" + account
+ ",date=" + getDate()
+ ",type=" + (getType() == CREDIT ? "Credit" : "Debit")
+ ",amount=" + getAmount()
+ "]";
}
}
Example5.java は 2 つの Account オブジェクトを作成します。コンストラクターの呼び出しにより Account オブジェクトの 1 つが各 Cashflow に渡されます。
例8.47 銀行取引のチュートリアル: Example5.java
public class Example6
{
public static void main(String[] args) throws Exception
{
Account acc1 = new Account(1);
Account acc2 = new Account(2);
Object[] cashflows =
{
new AllocatedCashflow(acc1,new SimpleDate("01/01/2007"),
TypedCashflow.CREDIT, 300.00),
new AllocatedCashflow(acc1,new SimpleDate("05/02/2007"),
TypedCashflow.CREDIT, 100.00),
new AllocatedCashflow(acc2,new SimpleDate("11/03/2007"),
TypedCashflow.CREDIT, 500.00),
new AllocatedCashflow(acc1,new SimpleDate("07/02/2007"),
TypedCashflow.DEBIT, 800.00),
new AllocatedCashflow(acc2,new SimpleDate("02/03/2007"),
TypedCashflow.DEBIT, 400.00),
new AllocatedCashflow(acc1,new SimpleDate("01/04/2007"),
TypedCashflow.CREDIT, 200.00),
new AllocatedCashflow(acc1,new SimpleDate("05/04/2007"),
TypedCashflow.CREDIT, 300.00),
new AllocatedCashflow(acc2,new SimpleDate("11/05/2007"),
TypedCashflow.CREDIT, 700.00),
new AllocatedCashflow(acc1,new SimpleDate("07/05/2007"),
TypedCashflow.DEBIT, 900.00),
new AllocatedCashflow(acc2,new SimpleDate("02/05/2007"),
TypedCashflow.DEBIT, 100.00)
};
new RuleRunner().runRules(new String[]{"Example6.drl"},cashflows);
}
}
Example6.drl ファイルのルールを確認して、各 Cashflow がどのように日付順で適用されるか確認し、残高を計算および出力します。
rule "Rule 06 - Credit"
when
$cashflow : AllocatedCashflow( $account : account,
$date : date, $amount : amount, type==TypedCashflow.CREDIT )
not AllocatedCashflow( account == $account, date < $date)
then
System.out.println("Credit: " + $date + " :: " + $amount);
$account.setBalance($account.getBalance()+$amount);
System.out.println("Account: " + $account.getAccountNo() +
" - new balance: " + $account.getBalance());
retract($cashflow);
end
rule "Rule 06 - Debit"
when
$cashflow : AllocatedCashflow( $account : account,
$date : date, $amount : amount, type==TypedCashflow.DEBIT )
not AllocatedCashflow( account == $account, date < $date)
then
System.out.println("Debit: " + $date + " :: " + $amount);
$account.setBalance($account.getBalance() - $amount);
System.out.println("Account: " + $account.getAccountNo() +
" - new balance: " + $account.getBalance());
retract($cashflow);
end
Cashflow をチェックする時に型を指定しないでください。これは、型に関係なくすべての Cashflow が日付順にチェックされるようにするためです。作業する口座を識別するために conditions が使用され、Cashflow の金額で更新するために consequences が使用されます。
Loading file: Example6.drl Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon Jan 01 00:00:00 GMT 2007,type=Credit,amount=300.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon Feb 05 00:00:00 GMT 2007,type=Credit,amount=100.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Sun Mar 11 00:00:00 GMT 2007,type=Credit,amount=500.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Wed Feb 07 00:00:00 GMT 2007,type=Debit,amount=800.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Fri Mar 02 00:00:00 GMT 2007,type=Debit,amount=400.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Sun Apr 01 00:00:00 BST 2007,type=Credit,amount=200.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Thu Apr 05 00:00:00 BST 2007,type=Credit,amount=300.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Fri May 11 00:00:00 BST 2007,type=Credit,amount=700.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon May 07 00:00:00 BST 2007,type=Debit,amount=900.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Wed May 02 00:00:00 BST 2007,type=Debit,amount=100.0] Debit: Fri Mar 02 00:00:00 GMT 2007 :: 400.0 Account: 2 - new balance: -400.0 Credit: Sun Mar 11 00:00:00 GMT 2007 :: 500.0 Account: 2 - new balance: 100.0 Debit: Wed May 02 00:00:00 BST 2007 :: 100.0 Account: 2 - new balance: 0.0 Credit: Fri May 11 00:00:00 BST 2007 :: 700.0 Account: 2 - new balance: 700.0 Credit: Mon Jan 01 00:00:00 GMT 2007 :: 300.0 Account: 1 - new balance: 300.0 Credit: Mon Feb 05 00:00:00 GMT 2007 :: 100.0 Account: 1 - new balance: 400.0 Debit: Wed Feb 07 00:00:00 GMT 2007 :: 800.0 Account: 1 - new balance: -400.0 Credit: Sun Apr 01 00:00:00 BST 2007 :: 200.0 Account: 1 - new balance: -200.0 Credit: Thu Apr 05 00:00:00 BST 2007 :: 300.0 Account: 1 - new balance: 100.0 Debit: Mon May 07 00:00:00 BST 2007 :: 900.0 Account: 1 - new balance: -800.0
8.5. 価格ルールのデシジョンテーブルの例
| 名前: | 価格決定のポリシー例 |
| メインクラス: | org.drools.examples.decisiontable.PricingRuleDTExample |
| タイプ: | Java アプリケーション |
| Rules ファイル: | ExamplePolicyPricing.xls |
| 目的: | スプレッドシートベースのデシジョンテーブルの実証。 |
8.5.1. 例の実行
PricingRuleDTExample.java ファイルを開き、Java アプリケーションとして実行します。次の出力が Console ウインドウに表示されます。
Cheapest possible BASE PRICE IS: 120 DISCOUNT IS: 20
stateless session が作成されます。違いはルールが追加される方法にあります。
DecisionTableConfiguration dtableconfiguration =
KnowledgeBuilderFactory.newDecisionTableConfiguration();
dtableconfiguration.setInputType( DecisionTableInputType.XLS );
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
Resource xlsRes = ResourceFactory.newClassPathResource( "ExamplePolicyPricing.xls",
getClass() );
kbuilder.add( xlsRes,
ResourceType.DTABLE,
dtableconfiguration );
DecisionTableConfiguration オブジェクトの使用に注目してください。この入力型は DecisionTableInputType.XLS に設定されます。
注記
Driver と Policy の 2 つの fact types が使用されます。両方のファクトタイプのデフォルト値が使用されます。Driver の年齢は 30 歳で、以前に保険の請求がなく、リスクプロファイルは LOW になります。ドライバーが申請している Policy は COMPREHENSIVE で、まだ承認されていません。
8.5.2. ディシジョンテーブル

図8.10 デシジョンテーブルの設定
RuleSet 宣言がパッケージ名を提供することに注意してください。Variables (グローバル変数用) や Imports (クラスのインポートに使用) など、他のオプション項目をここに追加することも可能です。この例では、ルールの名前空間は fact classes の名前空間と同じであるため、省略されています。
RuleTable 宣言があります。後続の Pricing Bracket が、生成されるルールの名前のプレフィックスとして割り当てられます。
CONDITION または ACTION があります。これは、列の目的を示しています。列が条件の一部または生成されたルールの結果を形成するかどうかを決定します。
$1 および $2 (コンマ区切りの値で入力)、locationRiskProfile および priorClaims (それぞれの列に存在) を使用します。
action 列に設定されます。ここにメッセージのログを記録することも可能です。

図8.11 基本保険料の計算
注記
120 になります。

図8.12 割引の計算
COMPREHENSIVE 契約を申請しています。これにより、20 パーセントの割引が適応されます。
注記
重要
8.6. ペットストアの例
| 名前: | ペットストア |
| メインクラス: | org.drools.examples.petstore.PetStoreExample |
| タイプ: | Java アプリケーション |
| Rules ファイル: | PetStore.drl |
| 目的: | アジェンダグループ、グローバル変数、およびグラフィカルユーザーインターフェースの統合 (ルール内からのコールバックを含む) の使用について実証します。 |
Rules ファイル内には アジェンダグループ および 自動フォーカス 機能の使用方法を説明する例があります。さらに、この例は Java と MVFLEX 式言語のダイアレクトを組み合わせる方法も説明し、accumulate の使用やルールセット内から Java 関数を読み出す方法についても実証します。
PetStore.java ファイルに格納されます。このコードは次のプリンシパルクラスを定義します (このコードには、Swing イベントを処理するために使用されるマイナークラスも複数含まれます)。
Petstore-main()メソッドが含まれます。PetStoreUI- Swing ベースのグラフィカルユーザーインターフェースを作成および表示します。マウスやボタンのクリックなど、主にさまざまな GUI イベントに応答する小型のクラスが複数含まれています。TableModel- テーブルデータを保持します。SwingAbstractTableModelクラスを拡張する Java Bean として考慮してください。CheckoutCallback- このクラスにより、グラフィカルユーザーインターフェースがルールと対話できます。Ordershow- ユーザーが購入を希望するアイテムを保持します。Purchase- 注文と購入済み製品の詳細を保存します。Product- 購入可能な製品の価格情報やその他の情報を保持する Java Bean です。
注記
Java Beans 形式になります。ここでは Swing について詳しく説明しませんが、 Sun Microsystems の Web サイト http://java.sun.com/docs/books/tutorial/uiswing/ に使用方法のチュートリアルがあります。
Petstore.java ファイルの Java コードの一部は次の通りです。
例8.48 PetStore.main にペットストアの RuleBase を作成
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newClassPathResource( "PetStore.drl",
PetStore.class ),
ResourceType.DRL );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
// Create the stock.
Vector<Product> stock = new Vector<Product>();
stock.add( new Product( "Gold Fish", 5 ) );
stock.add( new Product( "Fish Tank", 25 ) );
stock.add( new Product( "Fish Food", 2 ) );
// A callback is responsible for populating the
// Working Memory and for firing all rules.
PetStoreUI ui = new PetStoreUI( stock,
new CheckoutCallback( kbase ) );
ui.createAndShowGUI();
DRL ファイルからルールをロードします。ファクトが即座にアサートされ実行される他の例とは異なり、この例ではファクトのアサートと実行が遅延されます。これは、Vector オブジェクトである stock を許可するコンストラクターを使用して PetStoreUI オブジェクトが作成される最後から 2 つ目の行によって決定されます。これは製品と、この直前にロードされた rule-base が含まれる CheckoutCallback クラスのインスタンスを収集します。
CheckoutCallBack.checkout() メソッドによって呼び出されます。これは、ユーザーが Checkout ボタンをクリックするとトリガーされます。
public String checkout(JFrame frame, List<Product> items) {
Order order = new Order();
// Iterate through list and add to cart
for ( Product p: items ) {
order.addItem( new Purchase( order, p ) );
}
// Add the JFrame to the ApplicationData to allow for user interaction
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
ksession.setGlobal( "frame", frame );
ksession.setGlobal( "textArea", this.output );
ksession.insert( new Product( "Gold Fish", 5 ) );
ksession.insert( new Product( "Fish Tank", 25 ) );
ksession.insert( new Product( "Fish Food", 2 ) );
ksession.insert( new Product( "Fish Food Sample", 0 ) );
ksession.insert( order );
ksession.fireAllRules();
// Return the state of the cart
return order.toString();
}
TableModel からこのリストが取得されます。
for ループは注文商品のリストを Order Java Bean に変換します (PetStore.java にあります)。
注記
注記
TableModel の内容がセッションの working memory にコピーされます。
working memory への呼び出しが 9 つあります。最初の呼び出しは新しい working memory ステートフルナレッジセッション (Knowledge Base 内) を作成します (この Knowledge Base は、main() メソッドで作成された CheckoutCallBack クラスで渡されることに注意してください)。
working memory と注文リストの両方に格納されます。最後の呼び出しは標準の fireAllRules() メソッドです。次に、ルールが実行された時にこのメソッドが何を行うか見てみましょう。
例8.49 パッケージ、インポート、グローバル、およびダイアレクト - PetStore.drl より抜粋
package org.drools.examples
import org.drools.WorkingMemory
import org.drools.examples.PetStore.Order
import org.drools.examples.PetStore.Purchase
import org.drools.examples.PetStore.Product
import java.util.ArrayList
import javax.swing.JOptionPane;
import javax.swing.JFrame
global JFrame frame
global javax.swing.JTextArea textArea
PetStore.drl ファイルの最初の部分には標準の package および import ステートメント (さまざまな Java クラスをルールが使用できるようにする) が含まれています。この他に、2 つの グローバル変数、frame および textArea があります。これらのグローバル変数は、Swing の JFrame コンポーネントおよび Textarea コンポーネントへの参照を保持します (Rules 変数は実行後すぐに期限切れとなりますが、グローバル変数はセッションの有効期間中、値が保持されます)。
PetStore.drl ファイルの最後の部分から抜粋したものです。これには、ルールによって参照される 2 つの関数が含まれています (これら 2 つの関数については次の項で取り上げます)。
function void doCheckout(JFrame frame, WorkingMemory workingMemory)
{
Object[] options = {"Yes","No"};
int n = JOptionPane.showOptionDialog(frame,
"Would you like to checkout?","",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,options,options[0]);
if (n == 0) {workingMemory.setFocus( "checkout" );}
}
function boolean requireTank(JFrame frame, WorkingMemory workingMemory,
Order order, Product fishTank, int total)
{
Object[] options = {"Yes","No"};
int n = JOptionPane.showOptionDialog(frame,
"Would you like to buy a tank for your " +
total + " fish?",
"Purchase Suggestion",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,options,options[0]);
System.out.print( "SUGGESTION: Would you like to buy a tank for your "
+ total + " fish? - " );
if (n == 0) {
Purchase purchase = new Purchase( order, fishTank );
workingMemory.insert( purchase );
order.addItem( purchase );
System.out.println( "Yes" );
} else {
System.out.println( "No" );
}
return true;
}
注記
Rules ファイルに格納すると、ペットストアの例がコンパクトになります。実際には、同じルールパッケージ内の個別のファイルに関数を格納するか、標準の Java クラスの静的メソッドとして個別のファイルに関数を格納する必要があります。これらの関数は import function my.package.Foo.hello でインポートします。
doCheckout()は、ユーザーに支払い (チェックアウト) するかどうかを尋ねるダイアログボックスを表示します。支払いしたい場合、フォーカスがcheckOutアジェンダグループに設定され、そのグループ内のルールに潜在的な実行パーミッションが与えられます。requireTank()は、水槽を購入したいかどうかをユーザーに尋ねるダイアログボックスを表示します。購入したい場合、working memoryの注文リストに追加されます。
auto-focus 属性が true に設定されていることが部分的に関係します)。
例8.50 アイテムをワーキングメモリーに格納 - PetStore.drl ファイルより抜粋
/// Insert each item in the shopping cart into the Working Memory
// Insert each item in the shopping cart into the Working Memory
rule "Explode Cart"
agenda-group "init"
auto-focus true
salience 10
dialect "java"
when
$order : Order( grossTotal == -1 )
$item : Purchase() from $order.items
then
insert( $item );
kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "show items" ).setFocus();
kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "evaluate" ).setFocus();
end
PetStore.drl) が算出されていないすべての注文に対して一致します。順番に各購入アイテムをループします。ルール名、salience (ルールが実行される順番を提示)、ダイアレクト (Java に設定) など、Explode Cart ルールにはすでに説明した項目があります。また、ルールには新しいアイテムが 3 つあります。
agenda-group "init"- アジェンダグループの名前です。この例では、グループに 1 つのルールしか存在しません。しかし、Java コードとルール結果はどちらもフォーカスをこのグループに設定しないため、実行する機会を得られるかどうかは次の属性に依存します。auto-focus trueは、Java コードからfireAllRules()が呼び出された時に、アジェンダグループの唯一のルールが実行する機会を得られるようにします。drools.setFocus()はshow itemsおよびevaluateアジェンダグループに順番にフォーカスを提供し、ルールの実行を許可します (実際には、確実に注文アイテムがメモリーに挿入されるよう、注文アイテムはすべてループされます。他のルールはその後順次実行されます)。
show items および evaluate アジェンダグループのルールを表しています。
例8.51 GUI のアイテムを表示 - PetStore.drl ファイルより抜粋
rule "Show Items" agenda-group "show items" dialect "mvel" when $order : Order( ) $p : Purchase( order == $order ) then textArea.append( $p.product + "\n"); end
show items アジェンダグループが最初に呼び出されます。これには、Show Items (小文字と大文字の違いに注意してください) と呼ばれるルールのみがあります。このルールは、各購入のログ詳細をテキストエリア (GUI 画面の下部) へ移動します (この操作に使用される textArea 変数は先に説明したグローバル変数の 1 つです)。
evaluate アジェンダグループも先にリストされた explode cart よりフォーカスを取得します。このアジェンダグループには、Free Fish Food Sample と Suggest Tank の 2 つのルールがあります。
例8.52 Evaluate アジェンダグループ: PetStore.drl ファイルより抜粋
// Free Fish Food sample when we buy a Gold Fish if we have not already //bought Fish Food and dont already have a Fish Food Sample rule "Free Fish Food Sample" agenda-group "evaluate" dialect "mvel" when $order : Order() not ( $p : Product( name == "Fish Food") && Purchase( product == $p ) ) not ( $p : Product( name == "Fish Food Sample") && Purchase( product == $p ) ) exists ( $p : Product( name == "Gold Fish") && Purchase( product == $p ) ) $fishFoodSample : Product( name == "Fish Food Sample" ); then System.out.println( "Adding free Fish Food Sample to cart" ); purchase = new Purchase($order, $fishFoodSample); insert( purchase ); $order.addItem( purchase ); end // Suggest a tank if we have bought more than 5 gold fish and do not // already have one rule "Suggest Tank" agenda-group "evaluate" dialect "java" when $order : Order() not ( $p : Product( name == "Fish Tank") && Purchase( product == $p ) ) ArrayList( $total : size > 5 ) from collect( Purchase ( product.name == "Gold Fish" ) ) $fishTank : Product( name == "Fish Tank" ) then requireTank(frame, drools.getWorkingMemory(), $order, $fishTank, $total); end
Free Fish Food Sample ルールは以下の場合のみ実行されます。
- ユーザーが魚の餌を持っていない場合。
- ユーザーが魚の餌の無料サンプルを持っていない場合。
- ユーザーの注文に金魚が含まれる場合。
Fish Food Sample) が生成され、Fish Food Sample の注文に追加されます。
Suggest Tank ルールは次の 2 つの条件が満たされた場合のみ実行されます。
- ユーザーが水槽を注文していない場合。
- ユーザーが金魚関係の製品を 6 つ以上注文した場合。
requireTank() 関数が呼び出されます。確認後、水槽が注文に追加されます。ルールが requireTank() 関数を呼び出す時、ルールがグローバル frame 変数を渡すことに注意してください。これは、関数が Swing グラフィカルユーザーインターフェースのハンドルを持つからです。
do checkout と呼ばれます。
例8.53 チェックアウトの実行: PetStore.drl ファイルより抜粋
rule "do checkout" dialect "java" when then doCheckout(frame, drools.getWorkingMemory()); end
do checkout ルールには設定されたアジェンダグループや auto-focus 属性がありません。そのため、デフォルトのアジェンダグループの一部と見なされ、明示的なフォーカスを受け取るよう設定されたルールがすべて完了したときに、フォーカスを自動的に受け取ります。
doCheckout() 関数を呼び出します。この場合、ルールはグローバル frame 変数を渡し、Swing グラフィカルユーザーインターフェースのハンドルを関数に付与します (上記の通り、doCheckout() 関数はユーザーに確認ダイアログボックスを表示します。確認後、関数はフォーカスを checkout アジェンダグループに渡し、次のルールセットを実行できるようにします)。
例8.54 チェックアウトのルール: PetStore.drl ファイルより抜粋
rule "Gross Total"
agenda-group "checkout"
dialect "mvel"
when
$order : Order( grossTotal == -1)
Number( total : doubleValue ) from accumulate( Purchase ( $price : product.price ),sum( $price ) )
then
modify( $order ) { grossTotal = total };
textArea.append( "\ngross total=" + total + "\n" );
end
rule "Apply 5% Discount"
agenda-group "checkout"
dialect "mvel"
when
$order : Order( grossTotal >= 10 && < 20 )
then
$order.discountedTotal = $order.grossTotal * 0.95;
textArea.append("discountedTotal total="+$order.discountedTotal+"\n");
end
rule "Apply 10% Discount"
agenda-group "checkout"
dialect "mvel"
when
$order : Order( grossTotal >= 20 )
then
$order.discountedTotal = $order.grossTotal * 0.90;
textArea.append("discountedTotal total="+$order.discountedTotal+"\n");
end
checkout アジェンダグループには 3 つのルールがあります。
Gross Total- まだ実行されていない場合、製品価格を累積し、合計をworking memoryに保存して、SwingText Areaに表示します (textAreaグローバル変数を使用)。- 総合計が 10 と20 の間になる場合、
Apply 5% Discountが割引後の合計を計算し、working memoryへ追加してテキストエリアに表示します。 - 総合計が 20 を越える場合、
Apply 10% Discountが割引後の合計を計算し、working memoryへ追加してテキストエリアに表示します。
PetStore.java というファイルには main() メソッドが含まれているため、コマンドラインまたは IDE 内より標準の Java アプリケーションとして実行することができます (クラスパスが適切に設定されていることを前提とします)。

図8.13 起動直後の Pet Store Demo
main()メソッドが実行され、ルールベースがロードされますが、ルールはまだ実行されません (ここまで、このコードがルールと何らかの関係がある唯一のコードになります)。- 新しい
PetStoreUIオブジェクトが作成され、ルールベースのハンドルが付与されます。このハンドルは後で使用します。 - さまざまな Swing コンポーネントが操作を実行します。この時点で初めて上記の画面が表示され、ユーザーによる入力を待ちます。

図8.14 製品が選択された Pet Store Demo
注記
TableModel オブジェクトへ追加して右上の部分に表示します (これは Model View Controller 設計パターンの従来の実装であることに注意してください)。
- Checkout ボタンがクリックされると、Swing クラスによって
CheckOutCallBack.checkout()メソッドが呼び出されます。このメソッドはTableModelオブジェクト (グラフィカルユーザーインターフェースの右上に表示されます) よりデータを挿入し、さらにセッションのworking memoryに置きます。その後、ルールが実行されます。 Explode Cartルールは、自動フォーカス設定がtrueであるため、最初に実行されます。カート内のすべての製品をループし、確実にこれらの製品がworking memoryに存在するようにします。次に、Show ItemsおよびEvaluationアジェンダグループに実行パーミッションを与えます。これらのグループにあるルールは、カートの内容をテキストエリア (ウインドウの下部) に追加します。さらに、魚の餌をユーザーに無料で提供するか決定し、水槽を購入するかどうかをユーザーに尋ねます。水槽購入の画面は次のようになります。
図8.15 水槽を購入しますか?
Do Checkoutルールが次に実行されます。これは、現在フォーカスを持つアジェンダグループが他になく、デフォルトのアジェンダグループの一部であるためです。このルールは、次の質問が含まれるダイアログボックスを表示するdoCheckout()関数を常に呼び出します。"Would you like to Checkout?"
doCheckout()関数はフォーカスをcheckoutアジェンダグループに設定し、このグループのルールが実行する選択肢を与えます。checkoutアジェンダグループのルールは「カート」の内容を表示し、適切な割引を適用します。- 下図の通り、さらに製品をチェックアウトするか (これによりルールが再度実行されます) またはグラフィカルユーザーインターフェースが閉じられるかに応じて、Swing はユーザーの入力を待ちます。

図8.16 ルールがすべて実行され、アプリケーションが終了
System.out 呼び出しをさらに追加して、イベントの流れを実証することもできます。上記の例に表された Console の出力は次のようになります。
例8.55 ペットストア GUI 実行後のコンソール出力
Adding free Fish Food Sample to cart SUGGESTION: Would you like to buy a tank for your 6 fish? - Yes
8.7. 数独の例
| 名前: | 数独 |
| メインクラス: | org.drools.examples.sudoku.Main |
| タイプ: | Java アプリケーション |
| Rules ファイル: | sudokuSolver.drl, sudokuValidator.drl |
| 目的: | 論理的な問題を解決する方法を実証し、複雑なパターンマッチングを使用する方法を説明します。 |
working memory への変更に基づいて画面を更新し、 JBoss Rules とグラフィカルユーザーインターフェースベースのアプリケーションを統合する方法についても説明します。
8.7.1. 数独の概要
8.7.2. 例の実行
drools-examples ファイルをダウンロードし、インストールします。その後、 java org.drools.examples.sudoku.Main を実行します (この例には Java 5 が必要です)。比較的簡単な一部が埋められたグリッドがウインドウに表示されます。

図8.17 一部が埋められたグリッド
rules engine が残りの値を埋めます。Console はルールが実行されるとルールの詳細情報を表示し、パズルの解き方を実証します。
Rule #3 determined the value at (4,1) could not be 4 as this value already exists in the same column at (8,1) Rule #3 determined the value at (5,5) could not be 2 as this value already exists in the same row at (5,6) Rule #7 determined (3,5) is 2 as this is the only possible cell in the column that can have this value Rule #1 cleared the other PossibleCellValues for (3,5) as a ResolvedCellValue of 2 exists for this cell. Rule #1 cleared the other PossibleCellValues for (3,5) as a ResolvedCellValue of 2 exists for this cell. ... Rule #3 determined the value at (1,1) could not be 1 as this value already exists in the same zone at (2,1) Rule #6 determined (1,7) is 1 as this is the only possible cell in the row that can have this value Rule #1 cleared the other PossibleCellValues for (1,7) as a ResolvedCellValue of 1 exists for this cell. Rule #6 determined (1,1) is 8 as this is the only possible cell in the row that can have this value
engine は 2 番目の rule base を処理します。これにより、解答が完全で有効であることがチェックされます。この例では、すべてが完全で有効であるため、結果として Solve ボタンが無効になり、以下のメッセージが表示されます。
Solved (1052ms)

図8.18 解決済みのグリッド
5 が 2 つあります)。

図8.19 無効なグリッド
Validation Rule Set が発見した問題をすべて Console に出力することを確認してください。
There are two cells on the same column with the same value at (6,0) and (4,0) There are two cells on the same column with the same value at (4,0) and (6,0) There are two cells on the same row with the same value at (2,4) and (2,2) There are two cells on the same row with the same value at (2,2) and (2,4) There are two cells on the same row with the same value at (6,3) and (6,8) There are two cells on the same row with the same value at (6,8) and (6,3) There are two cells on the same column with the same value at (7,4) and (0,4) There are two cells on the same column with the same value at (0,4) and (7,4) There are two cells on the same row with the same value at (0,8) and (0,0) There are two cells on the same row with the same value at (0,0) and (0,8) There are two cells on the same column with the same value at (1,2) and (3,2) There are two cells on the same column with the same value at (3,2) and (1,2) There are two cells in the same zone with the same value at (6,3) and (7,3) There are two cells in the same zone with the same value at (7,3) and (6,3) There are two cells on the same column with the same value at (7,3) and (6,3) There are two cells on the same column with the same value at (6,3) and (7,3)
engine が実際に見つけられないものがあります。例を確認するには、[]、[]、[] の順にクリックします。一部が埋められたグリッドがロードされます。
engine に提供する必要があります。
8.7.3. Java ソースとルールの概要
/src/main/java/org/drools/examples/sudoku ディレクトリにあります。2 つの DRL ルール定義ファイルは /src/main/rules/org/drools/examples/sudoku ディレクトリにあります。
org.drools.examples.sudoku.swing パッケージには、数独パズルのフレームワークを実装するクラスセットが含まれています。
注記
SudokuGridModel インターフェースを実装します (値はセルの値が決定していないことを示す null である場合があります)。
SudokuGridView は Swing コンポーネントです。SudokuGridModel の実装をグラフィカルに表すことができます。
SudokuGridEvent および SudokuGridListener はモデルのステート変更をビューに通知するため使用されます。セルの値が解決または変更されると、イベントが実行されます。JTable など、他の Swing コンポーネントで使用されるモデルビューコントローラーパターンの知識があるユーザーは、この挙動を理解できるはずです (このような概念を実証するため、SudokuGridSamples は部分的に完成されたパズルを複数提供します)。
org.drools.examples.sudoku.rules パッケージには、 JBoss Rules に基づいた SudokuGridModel の実装が含まれています。AbstractCellValue を拡張し、グリッドの特定セルの値を表す 2 つの Java オブジェクトが使用されます。この値には、セルの行と列の場所、値が含まれる 3x3 ゾーンのインデックス番号、およびセルに保持される実際の番号が含まれます。
PossibleCellValue はセルの値が現在不明であることを示しています (どのセルにも 2 から 9 個の可能な値があります)。
ResolvedCellValue は、セルの値が決定されたことを示します。セルの解決された値は 1 つのみです。
DroolsSudokuGridModel は SudokuGridModel を実装します。これは、一部指定されたセルのアレイ (最初は 2 次元) を CellValue Java オブジェクトのセットに変換します。これにより、solverSudoku.drl ルールファイルに基づいて working memory セッションが作成されます。また、CellValue オブジェクトも working memory に挿入します。
solve() メソッドが呼び出されると、パズルを解こうとする fireAllRules() が呼び出されます。
DroolsSudokuGridModel は WorkingMemoryListener を working memory にアタッチします。これにより、パズルが解決された時に挿入および取り消しイベントで呼び戻すことが可能です。新しい ResolvedCellValue が working memory に挿入されると、このコールバックにより実装は SudokuGridEvent を SudokuGridListener クライアントに対して実行することができ、リアルタイムで更新します。
"solver" working memory によってルールがすべて実行されると、DroolsSudokuGridModel が 2 番目のルールセットを実行します (これらのルールは validatorSudoku.drl ファイルから取得されます)。これらのルールは同じ Java オブジェクトのセットと動作し、結果となるグリッドが有効で完全な解答であるかを判断することが目的です。
注記
org.drools.examples.sudoku.Main クラスは上記のコンポーネントを組み合わせる Java アプリケーションを実装します。
org.drools.examples.sudoku パッケージには、solverSudoku.drl と validator.drl の 2 つの DRL ファイルが含まれています。solverSudoku.drl は数独パズルを解くために使用されるルールを定義します。validator.drl は、working memory の現在の状態が有効な解答を表すかどうかをテストするルールを定義します。これらのルールセットはPossibleCellValue および ResolvedCellValue オブジェクトをファクトとして使用します。また、両方とも実行時に Console ウィンドウへデータを出力します。
注記
WorkingMemoryListener を使用して出力をユーザーに表示します。
8.7.4. 検証ルール
validatorSudoku.drl ファイルにある検証ルールが従うプロセスは次の通りです。
- 最初のルールは、
working memoryにPossibleCellValueオブジェクトがないことを確認します (パズルが解かれると、各セルにResolvedCellValueオブジェクト 1 つのみが存在するはずです)。 - 他の 3 つのルールは
ResolvedCellValueオブジェクトすべてと一致し、$resolved1 という変数へバインドされます。そして、同じ値が含まれ、同じ行、列および 3x3 ゾーンにあるResolvedCellValuesを探します。 - これらのルールが実行されると、解答が無効である理由を示すメッセージが文字列のグローバルリストに追加されます。
DroolsSudokoGridModelはルールセットを実行する前にこのリストをインジェクトします (また、fireAllRules()を呼び出した後にこのリストが空であるかどうかを確認します。空でない場合、リストに全文字列を出力します。空でない場合、すべての文字列がリストに出力され、グリッドが解決していないことを示すフラグが設定されます)。
8.7.5. ルールの解決
solverSudoku.drl ファイルにある「解決」ルールの前に実行されるさらに複雑なプロセスです。
- ルール
#1は、無効な解答が存在する行と列に対応するPossibleCellValuesのworking memoryを消去します。他の複数のルールは、セルに値が必要であると判断した後、特定行および列のworking memoryにResolvedCellValuesを挿入します。このルールは重要であるため、他のルールよりも大きな salience の値が指定されます。そのため、左側がtrueになると、即座にactivationsがアジェンダの最上部に移動され、実行されます。これにより、他のルールが誤って実行されないようにします。このルールはResolvedCellValue上でupdate()も実行します (これは、working memoryにアタッチされたWorkingMemoryListenersへ JBoss Rules がイベントを送信するようにルールが変更されていなくても発生します。このようなイベントを実行すると、ルールがルール自体を更新できます)。これにより、グラフィカルユーザーインターフェースがグリッドの新しい状態を表示します。 - ルール
#2は、可能な値が 1 つのみであるグリッドのセルを識別します。when句の最初の行は、working memoryのPossibleCellValueオブジェクトすべてと一致します。2 行目はnotキーワードの使用法を表しています。注記
このルールは、異なる値を持つ同じ行と列のPossibleCellValueが他にない場合のみ実行されます。このルールが実行されると、その行と列の単一のPossibleCellValueがworking memoryから取り消され、同じ値のResolvedCellValueに置き換えられます。 - ルール
#3は、ResolvedCellValueと同じ値がある場合に行からPossibleCellValuesを削除します。つまり、解決された値がセルに含まれる場合、パズルのルールに従うため同じ値が含まれる同じ行の他のセルを消去する必要があります。when句の最初の行は、working memoryのResolvedCellValueオブジェクトをすべて見つけます。2 番目の行は、これらのResolvedCellValueオブジェクトと同じ行と値を持つPossibleCellValuesを見つけます。 このようなPossibleCellValuesが見つかると、ルールがアクティベートされ、実行された時に取り消されます。これは、これらのセルに対する適切な解答ではないためです。 - ルール
#4と#5はルール#3と同じように動作しますが、ResolvedCellValuesと競合する冗長なPossibleCellValuesがないか列とゾーンをチェックします。 - ルール
#6は、セルの可能な値が該当する行に 1 つだけある場合をチェックします。左側の最初の行はworking memoryのPossibleCellValueファクトすべてと一致し、結果を複数のローカル変数に格納します。2 行目は、同じ値を持つ他のPossibleCellValueオブジェクトが同じ行に存在しないことをチェックします。3 - 5 行目は、同じ値を持つResolvedCellValueが同じゾーン、行、または列に存在しないことをチェックします (これは、このルールが不完全な状態で実行されないようにするためです)。注記
また、3 - 5 行目を行を削除し、ルール#3、#4、および#5の salience の値を大きくしてこれらのルールが常にルール#6、#7、および#8の前に実行されるようにすることも可能です。ルールが実行された場合、$possibleはセルの値を表す必要があります。そのため、これを取り消し、同等のResolvedCellValueに置き換えます (これはルール #2 のプロセスと同じです)。 - ルール
#7と#8はルール#2と同じように動作しますが、#7はグリッドの該当列にある単一のPossibleCellValuesをチェックし、#8はグリッドの該当ゾーンにあるPossibleCellValuesをチェックします。 - ルール
#9は最も複雑です。これは「該当する値のペアが特定行の 2 つのセルにのみあり (たとえば、4と6の値はセル [0,3] および [0,5] の最初の行のみに存在できることを判断しました)、このセルのペアが他の値を保持できない場合、4 が含まれるペアと 6 が含まれるペアがどれであるは分かりませんが、これらの値がこれらの 2 つのセルになければならないことはわかっています。そのため、これらの値が同じ行の他の場所で発生する可能性を排除することができます」ということです。 - ルール
#10と#11は ルール#9と同じように動作しますが、#10は該当列に可能な値が 2 つのみ存在することをチェックし、#8は該当ゾーンに可能な値が 2 つのみ存在することをチェックします。
8.7.6. 将来的な開発に関する提言
- Agenda Groups: 段階的な実行 に 宣言ツール を使用します。この例では、「解決」 と 「検証」の 2 つの段階があることが分かるはずです。現在、これらは 2 つのルールベースファイルより実行されます。「解決」と「検証」のカテゴリーに分類し、すべてのルールに対してアジェンダグループを定義することが推奨されます。その後、すべてが単一の
rule-baseよりロードされます。engineはこれらを順番に即座に実行します。 - 自動フォーカス: このメソッドを使用し、通常のルール実行の例外に対応します。現時点では、入力データーまたは解決ルールのどちらかに不整合があった場合、そのまま無駄にルールを実行せずに、即座に報告した方がよいでしょう。すでに単一の
rule-baseが作成されているため、パズルの一貫性の検証に使用される各ルールに対して auto-focus 属性を定義します。 - 論理挿入: 不整合は
working memoryに誤ったデータがある場合のみ発生します。そのため、したがって、検証ルールが不整合を 理論的に挿入 すると言えます (誤ったデータが取り消されると、すぐに不整合はなくなります)。 session.iterateObjects(): 現時点では、グローバルリストは問題を記録するために使用されますが、session.iterateObjects( new ClassObjectFilter( Inconsistency.class ) )を介して必要な問題を呼び出すようstateful sessionに要求すると、より興味深いでしょう。inconsistencyを使用すると、不正なセルに色 (赤など) を付けて見やすくすることもできます。kcontext.getKnowledgeRuntime().halt(): ソフトウェアが検出したエラーを即座に報告した場合でも、ルールの評価を停止するようエンジンに伝える方法が必要です。これは、不整合が存在する時にhalt()コードを呼び出すルールをプログラミングして作成します。- Queries:
DroolsSudokuGridModelのgetPossibleCellValues(int row, int col)メソッドを見てください。実際に必要なCellValueオブジェクトを探しながらすべてのCellValueオブジェクトを繰り返すことが分かります。JBoss Rules クエリを使用すると、この処理の効率を向上することができます。必要なオブジェクトを返し、きれいに繰り返すクエリを定義します (必要に応じて他のクエリを定義します)。 - サービスとしてのグローバル: この変更の主な目的は、これに続くものを容易にすることですが、これ自体にも便利です。サービスとしてのグローバル の使用法について学ぶには、最初に
callbackを設定することが理想的です。この変更は、該当のセルに対してResolvedCellValueを見つける各ルールがグラフィカルユーザーインターフェースを「呼び出し」て、更新することができ、ユーザーにフィードバックを即座に提供することを意味します。また、最後に見つかったセルの番号を違う色に「塗り替え」、異なるルールの結果を素早く識別できるようにすることもできます。 - ステップバイステップ実行: 即座にユーザーフィードバックを提供できるようコールバックを設定すると、JBoss Rules の 制約実行 機能を使用できます。グラフィカルユーザーインターフェースにボタンを追加し、クリックした時に
fireAllRules( 1 )を呼び出して 1 つのルールを実行します。このようにして、ユーザーはエンジンが行っていることをステップごとに確認できます。
8.8. 数字当て
| 名前: | 数字当て |
| メインクラス: | org.drools.examples.numberguess.NumberGuessExample |
| タイプ: | Java アプリケーション |
| Rules ファイル: | NumberGuess.drl |
| 目的: | ルールを整理するためルールフローを使用する方法を実証します。 |
Number Guess の例を用いて、ルールの実行順序を制御するための rule-flow の使用方法について学びましょう。ルールのグループを実行する順序を明確に示すため、標準的なワークフロー図が使用されます。
例8.56 数字当てルールベースの作成
final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.drl", ShoppingExample.class ),ResourceType.DRL ); kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.rf", ShoppingExample.class ),ResourceType.DRF ); final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
add() メソッドを用いてルールをロードするプロセスは、前述の例と同じですが、任意の手順が 1 つ追加されます。この追加手順では、1 つの knowledge base でさまざまな rule-flow を使用する機能を指定するため、現在の rule-flow に NumberGuess.rf 行を追加します (この手順を省略すると、knowledge base が前述の例と同じ方法で作成されます)。
例8.57 ルールフローの開始
final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger (ksession, "log/numberguess"); ksession.insert( new GameRules( 100,5 ) ); ksession.insert( new RandomNumber() ); ksession.insert( new Game() ); ksession.startProcess( "Number Guess" ); ksession.fireAllRules(); logger.close(); ksession.dispose();
knowledge base を使用して「ステートフル」セッションを取得します。このセッションに ファクト (標準的な Java オブジェクトのこと) を挿入します。簡単にするため、この例ではすべてのクラスが NumberGuessExample.java という 1 つのファイルに含まれています。
GameRulesクラスは最大範囲と回答可能な回数を提供します。RandomNumberクラスは、自動的にゼロから 100 までの数字を生成し、getValue()メソッドを用いて挿入した後にルールで使用できるようにします。Gameクラスは以前回答した答えと、現在の回答数を追跡します。
重要
fireAllRules() メソッドを呼び出す前に、startProcess() メソッドを用いて以前ロードされたプロセスを開始します。
注記
working memory セッションが dispose() メソッドへの呼び出しによって消去されるようにすれば十分です。

図8.20 数字当ての例のルールフロー
NumberGuess.rf ファイルを開きます。外見上、アイコンは JBoss jBPM Workflow のアイコンと大変似ていて (同じではない)、このイメージは標準的なフローチャートと非常に似ています。
X-Stream より、人言が判読できる XML 形式で保存されます。
rule-flow 上のアイテムを選択する 前に 行います (または rule-flow の空白スペースをクリックした後)。その後、次のプロパティーが使用できるようになります。

図8.21 文字当てルールフローのプロパティー
注記
session.startprocess() メソッドが最初に実行した時に開始された rule-flow プロセスの ID 番号が表示されています。
Number Guess の rule-flow のノード型は次の通りです。
- start (緑の矢印) および end ノード (赤いボックス) は
rule-flowが開始および終了する場所を示します。 - RuleFlowGroup (シンプルな黄色のボックスによって示されます) は、本項の後半で説明する
DRLファイルのRuleFlowGroupsへマップします。たとえば、フローがToo HighRuleFlowGroupに達すると、補足的なruleflow-groupToo High 属性でマーク付けされたルールのみが実行を許可されます。 - action nodes (歯車のようなエンブレムを持つ黄色のボックスで示されます) は、標準的な Java メソッドの呼び出しを実行できます。この例では、ほとんどの
action nodesがSystem.out.printlnを呼び出し、ユーザーに何が起こっているか伝えます。 Guess CorrectやMore Guesses Joinなどの split および join nodes (青の楕円) は、制御のフローを分割 (さまざまな条件による) または再結合できる場所を示します。- arrows はさまざまなノード間のフローの方向を示します。
Guess RuleFlowGroup は、実行する Get User Guess ルールのみを許可します。これは、一致する ruleflow-group "Guess" の属性を持つ唯一のルールであるからです。
例8.58 ルールフローの特定場所でのみ実行されるルールの例
rule "Get user Guess"
ruleflow-group "Guess"
no-loop
when
$r : RandomNumber()
rules : GameRules( allowed : allowedGuesses )
game : Game( guessCount < allowed )
not ( Guess() )
then
System.out.println( "You have " + ( rules.allowedGuesses - game.guessCount )
+ " out of " + rules.allowedGuesses
+ " guesses left.\nPlease enter your guess from 0 to "
+ rules.maxRange );
br = new BufferedReader( new InputStreamReader( System.in ) );
i = br.readLine();
modify ( game ) { guessCount = game.guessCount + 1 }
insert( new Guess( i ) );
end
guessCount が allowedGuesses の数よりも小さく (GameRules クラスより読み取り)、ユーザーが正しい数字を回答していない場合に、左側 (when 句) は RandomNumber オブジェクトが working memory に挿入されるたびにアクティベートされるよう指示します。
then が使用されます) はユーザーにメッセージを出力し、System.in よりユーザーの応答が到達するのを待ちます。この入力を受け取った後 (return キーが押されるまで System.in がブロックします)、回答数と実際の回答を更新または変更し、これらが working memory で使用可能になります。
Rules の残りの部分は比較的簡単です。ダイレクトが MVEL に設定され、さまざまな Java クラスがインポートされることをパッケージが宣言します。このファイルには合わせて 5 つのルールが存在します。
Get User Guess(上記で検証されたルール)- 最大の回答を記録するルール
- 最小の回答を記録するルール
- 回答を検査し、不正解の場合にメモリーから取り消すルール。
- すべての回答が使用されたことをユーザーに通知するルール。
rule-flow との間を統合する 1 つのポイントが ruleflow-group 属性です。2 つ目のポイントは DRL ルールファイルと rule-flow.rf ファイルの間です。split nodes (青の楕円) はワーキングメモリーの値を使用して (ルールによって更新される) 実行するアクションのフローを決定します。この挙動を確認するには、Guess Correct Node をクリックします。Properties ビュー内より Constraints Editor を開きます (Constraints プロパティー行をクリックした後に表示される右側のボタンを押します)。以下が表示されます。

図8.22 GuessCorrect ノードの制約を編集
working memory のオブジェクトを参照できます。consequence (右側にコード化) として、左側の式が true である場合に制御のフローがこのノードに従います。

図8.23 GuessCorrect の Constraints Editor
NumberGuess.java の例には main() メソッドが含まれるため、標準の Java アプリケーション (BASH から、または IDE 経由) として実行できます。典型的数字当てゲームでは次の対話が行われます (数字はユーザーによって入力されます)。
例8.59 数字当てプログラムが人間に勝った場合のコンソールの出力例
You have 5 out of 5 guesses left. Please enter your guess from 0 to 100 50 Your guess was too high You have 4 out of 5 guesses left. Please enter your guess from 0 to 100 25 Your guess was too low You have 3 out of 5 guesses left. Please enter your guess from 0 to 100 37 Your guess was too low You have 2 out of 5 guesses left. Please enter your guess from 0 to 100 44 Your guess was too low You have 1 out of 5 guesses left. Please enter your guess from 0 to 100 47 Your guess was too low You have no more guesses The correct guess was 48
NumberGuessExample.javaのMain()メソッドはRuleBaseをロードしてStatefulSessionを取得し、Game、GameRules、およびRandomNumber(ターゲット番号が含まれる) オブジェクトを挿入します。このメソッドは使用されるプロセスフローを設定し、すべてのルールを実行します。そして、制御がRuleFlowへ移ります。NumberGuessExample.rfRuleFlowがStartノードで開始されます。- 制御が
Guessノードに移り、More Guesses結合ノードより移行します。 GuessノードでGet User GuessRuleFlowGroupが有効になります。この場合、Guessルール (NumberGuess.drlファイルに存在する) がトリガーされます。ユーザーにメッセージを表示し、応答を受け取って、メモリーに格納します。制御が次のノードであるGuess Correctに移ります。- このノードでは、制約が現在のセッションを検証し、取るべきパスを決定します。4. で回答した数字が大きすぎるまたは小さすぎる場合、フローは以下の両方を持つパスに移ります。
- 通常の Java コードを使用して下記のステートメントのいずれかを出力するアクションノード。
Too high
Too low
Rulesファイル内より最大または最小の回答ルールがトリガーされるようにするRuleFlowGroup。
フローはこれらのノードから 6. で指定されたノードに移ります。4. の回答が正しい場合、通常の Java コードを持つアクションコードが次のステートメントを出力します。You guessed correctly.
ここには、結合ノードがあるため (ルールフロー終了の直前) 、no-more-guessesパス (7.) がRuleFlowも終了します。 - 制御が結合ノードを介して
RuleFlowへ移り、Guess IncorrectRuleFlowGroup(working memoryより回答を取り消すようルールをトリガーします) へ移動します。その後、More Guesses決定ノードへ移動します。 More Guesses決定ノード (ルールフローの右側) は制約を使用して (ルールがworking memoryに格納した値を確認して)、これ以上回答できるかどうかを決定します。回答可能な場合は 3. に戻ります。これ以上回答できない場合は、以下を表示するルールをトリガーするRuleFlowGroupを介して、制御がワークフローの最後に移動します。You have no more guesses
- 正しい数字が回答されるか、回答回数を使い果たすまで、3. から 7. のループが継続します。
8.9. Miss Manners とベンチマーキング
| 名前: | Miss Manners |
| メインクラス: | org.drools.benchmark.manners.MannersBenchmark |
| タイプ: | Java アプリケーション |
| Rules ファイル: | manners.drl |
| 目的: | Manners ベンチマークについて説明し、深さの競合解決 について取り上げます。 |
8.9.1. はじめに
8.9.1.1. ベンチマーキングのスクリプト
- Miss Manners は 深さ優先 探索を使用して座席の配置を決定します。男性と女性を交互に配置し、共通する趣味を持つ人が隣同士になるようにします。
- Waltz は線画を 3 次元にします。3 次元にするため、線をラベル付けし、制約伝播 を使用します。
- WaltzDB は
Waltzの汎用バージョンで、4 本以上の線による接合点をサポートし、データベースを使用します。 - 自動ルートプランナー (ARP) は、ロボット車両向けに設計されたルートプランナーです。
A*探索アルゴリズムを使用します。 - Weavera は、チャネルとボックス用の VLSI (Very Large Scale Integration) ルーターです。ブラックボード 技術を使用します。
注記
Miss Manners は、ルールエンジン業界の実質上の業界標準となるベンチマーキングテストです。この挙動は現在では広く知られるようになり、このテストを効率的に実行するよう多くのエンジンが最適化されたため、有用性は低くなっています。そのため、Waltz の人気が以前よりも高くなっています。
8.9.1.2. Miss Manners の実行フロー
Miss Manners は context インスタンスを使用し実行フローを制御します。次のアクティビティ図は、ルール実行と context の現状との関係を示すために分割されています。

図8.24 Miss Manners のアクティビティ図
8.9.1.3. データと結果
8.9.1.4. データ
(guest (name n1) (sex m) (hobby h1) ) (guest (name n2) (sex f) (hobby h1) ) (guest (name n2) (sex f) (hobby h3) ) (guest (name n3) (sex m) (hobby h3) ) (guest (name n4) (sex m) (hobby h1) ) (guest (name n4) (sex f) (hobby h2) ) (guest (name n4) (sex f) (hobby h3) ) (guest (name n5) (sex f) (hobby h2) ) (guest (name n5) (sex f) (hobby h1) ) (last_seat (seat 5) )
8.9.1.5. 結果
Assign Seat ルールが実行されると出力されます。各行に前の行よりも 1 大きい pid 値 があることに注意してください (この重要性については、本項の後半の Assign Seat ルールの説明で取り上げます)。 ls、rs、ln、および rn の略語は、左および右側の座席とこれらの座席に割り当てられたゲストの名前を表します。実際の実装では、長い属性名 (leftGuestName など) を使用しますが、本 ガイド では元の実装で使用された表記法が保持されます。
[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4] [Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3] [Seating id=4, pid=3, done=false, ls=3, rn=n3, rs=4, rn=n2] [Seating id=5, pid=4, done=false, ls=4, ln=n2, rs=5, rn=n1]
8.9.2. 深い分析
8.9.2.1. 不正行為
Miss Manners は、クロス積の結合 と Agenda のアクティビティを実行するよう設計されています。これを理解していないユーザーが、パフォーマンスを改善するために例を「微調整」してしまうことがありますが、これにより Manners ベンチマークのポートは意味がなくなります。Miss Manners における既知の不正行為とポートエラーのリストは次の通りです。
- ゲストの趣味を 1 つずつ 1 つのファクトとしてアサートせずに、アレイを使用。これにより、
cross productsが大幅に減少します。 - データシーケンスの変更。これにより、一致する量が減少し、実行速度が向上します。
- テストアルゴリズムが
first-best-matchコードのみを使用するようnot条件要素を変更。これは、基本的に 後向き連鎖 を実行するようテストテストアルゴリズムを変換します。このエリアの結果は、他の後向き連鎖ルールエンジンまたはMiss Mannersのポートのみに相当します。 rule engineがゲストと座席を時期尚早に一致するよう、contextを削除。適切なポートは、context startによりファクトが一致しないようにします。rule engineが 組み合わせ パターンマッチングを実行しないようにする。NOT CEの結果として 推論サイクル で取り消されるファクトがない場合、ポートが不完全。
8.9.2.2. 最初の座席の割り当て
START_UP に変わった後、アサートされたゲストごとに activations が作成されます。各 activation は単一の working memory アクションの結果として作成されるため、すべて同じ activation time タグを持ちます (アサートされる最後のゲストは、さらに大きな fact time タグを持ちます)。
注記
Assign Seat ルールには大きく影響します。
path がアサートされます。次に、findSeating ルールのアクティベーションを作成するため、Context 属性のステートが設定されます。
rule assignFirstSeat
when
context : Context( state == Context.START_UP )
guest : Guest()
count : Count()
then
String guestName = guest.getName();
Seating seating = new Seating( count.getValue(),
1,
true,
1,
guestName,
1,
guestName);
insert( seating );
Path path = new Path( count.getValue(), 1, guestName );
insert( path );
modify( count ) { setValue ( count.getValue() + 1 ) }
System.out.println( "assign first seat : "+seating+" : "+path);
modify( context ) { setState( Context.ASSIGN_SEATS ) }
end8.9.2.3. "findSeating" ルール
findSeating ルールは座席の配置を決定します。実行されると、アサートされた各ゲストに対して、アサートされた各座席配置の cross-product ソリューションを生成します (ルール自体とすでに割り当てられた選択済みソリューションを除く)。
rule findSeating
when
context : Context( state == Context.ASSIGN_SEATS )
$s : Seating( pathDone == true )
$g1 : Guest( name == $s.rightGuestName )
$g2 : Guest( sex != $g1.sex, hobby == $g1.hobby )
count : Count()
not ( Path( id == $s.id, guestName == $g2.name) )
not ( Chosen( id == $s.id, guestName == $g2.name, hobby == $g1.hobby) )
then
int rightSeat = $s.getRightSeat();
int seatId = $s.getId();
int countValue = count.getValue();
Seating seating = new Seating( countValue,
seatId,
false,
rightSeat,
$s.getRightGuestName(),
rightSeat + 1,
$g2.getName()
);
insert( seating );
Path path = new Path( countValue, rightSeat + 1, $g2.getName() );
insert( path );
Chosen chosen = new Chosen( seatId, $g2.getName(), $g1.getHobby() );
insert( chosen );
System.err.println( "find seating : "+seating+" : "+path+" : "+chosen);
modify( count ) {setValue( countValue + 1 )}
modify( context ) {setState( Context.MAKE_PATH )}
end=>[ActivationCreated(35): rule=findSeating [fid:19:33]:[Seating id=3, pid=2, done=true, ls=2, ln=n4, rs=3, rn=n3] [fid:4:4]:[Guest name=n3, sex=m, hobbies=h3] [fid:3:3]:[Guest name=n2, sex=f, hobbies=h3] =>[ActivationCreated(35): rule=findSeating [fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] [fid:5:5]:[Guest name=n4, sex=m, hobbies=h1] [fid:2:2]:[Guest name=n2, sex=f, hobbies=h1] =>[ActivationCreated(35): rule=findSeating [fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [fid:9:9]:[Guest name=n5, sex=f, hobbies=h1] [fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
注記
activations を作成することは意味がないように見えますが、Miss Manners の作成では優良なルール設計を目的としていないことを思い出してください。これは、cross-product matching 処理と Agenda 機能に対して完全にストレステストを行うために、意図的にルールセットが下手に記述されています。
注記
time タグ値は同じ 35 であることに注意してください。これは、ASSIGN_SEATS へ Context オブジェクトが変更になり、すべてアクティベートされたためです。OPS5 および LEX では、最後にアサートされた座席のアクティベーションが適切に実行されます。Depth では、accumulated fact time により、最後にアサートされる座席のアクティベーションが確実に実行されます。
8.9.2.4. "makePath" および "pathDone" ルール
makePath ルールは必ず pathDone の前に実行する必要があります。path オブジェクトは各座席の配置に対して最後までアサートされます (pathDone の条件は makePath のサブセットであることに注意してください。そのため、どのように makePath を最初に実行させるのか疑問に思う人がいるかもしれません)。
rule makePath when Context( state == Context.MAKE_PATH ) Seating( seatingId:id, seatingPid:pid, pathDone == false ) Path( id == seatingPid, pathGuestName:guestName, pathSeat:seat ) not Path( id == seatingId, guestName == pathGuestName ) then insert( new Path( seatingId, pathSeat, pathGuestName ) ); end
rule pathDone
when
context : Context( state == Context.MAKE_PATH )
seating : Seating( pathDone == false )
then
modify( seating ) {setPathDone( true )}
modify( context ) {setState( Context.CHECK_DONE)}
end
図8.25 Rete 図
activation time タグを持ちますが、makePath ルールの accumulate fact time タグの方が大きいため、makePath ルールが優先されます。
8.9.2.5. "Continue" および "Are We Done?" ルール
Are We Done は、最後の座席が割り当てられた時のみアクティベートします。この時点で、両方のルールが実行されます。makePath は常に pathDone よりも優先されますが、同じ理由で Are We Done は Continue よりも優先されます。
rule areWeDone
when
context : Context( state == Context.CHECK_DONE )
LastSeat( lastSeat: seat )
Seating( rightSeat == lastSeat )
then
modify( context ) {setState(Context.PRINT_RESULTS )}
endrule continue when context : Context( state == Context.CHECK_DONE ) then context.setState( Context.ASSIGN_SEATS ); update( context ); end
8.9.3. 出力の概要
Assign First Seat =>[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] =>[fid:14:14]:[Path id=1, seat=1, guest=n5] ==>[ActivationCreated(16): rule=findSeating [fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [fid:9:9]:[Guest name=n5, sex=f, hobbies=h1] [fid:1:1]:[Guest name=n1, sex=m, hobbies=h1] ==>[ActivationCreated(16): rule=findSeating [fid:13:13]:[Seating id=1 , pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [fid:9:9]:[Guest name=n5, sex=f, hobbies=h1] [fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]* Assign Seating =>[fid:15:17] :[Seating id=2 , pid=1 , done=false, ls=1, lg=n5, rs=2, rn=n4] =>[fid:16:18]:[Path id=2, seat=2, guest=n4] =>[fid:17:19]:[Chosen id=1, name=n4, hobbies=h1] =>[ActivationCreated(21): rule=makePath [fid:15:17] : [Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4] [fid:14:14] : [Path id=1, seat=1, guest=n5]* ==>[ActivationCreated(21): rule=pathDone [Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4]* Make Path =>[fid:18:22:[Path id=2, seat=1, guest=n5]] Path Done Continue Process =>[ActivationCreated(25): rule=findSeating [fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] [fid:7:7]:[Guest name=n4, sex=f, hobbies=h3] [fid:4:4] : [Guest name=n3, sex=m, hobbies=h3]* =>[ActivationCreated(25): rule=findSeating [fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] [fid:5:5]:[Guest name=n4, sex=m, hobbies=h1] [fid:2:2]:[Guest name=n2, sex=f, hobbies=h1], [fid:12:20] : [Count value=3] =>[ActivationCreated(25): rule=findSeating [fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [fid:9:9]:[Guest name=n5, sex=f, hobbies=h1] [fid:1:1]:[Guest name=n1, sex=m, hobbies=h1] Assign Seating =>[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, lnn4, rs=3, rn=n3]] =>[fid:20:27]:[Path id=3, seat=3, guest=n3]] =>[fid:21:28]:[Chosen id=2, name=n3, hobbies=h3}] =>[ActivationCreated(30): rule=makePath [fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3] [fid:18:22]:[Path id=2, seat=1, guest=n5]* =>[ActivationCreated(30): rule=makePath [fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3] [fid:16:18]:[Path id=2, seat=2, guest=n4]* =>[ActivationCreated(30): rule=done [fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]* Make Path =>[fid:22:31]:[Path id=3, seat=1, guest=n5] Make Path =>[fid:23:32] [Path id=3, seat=2, guest=n4] Path Done Continue Processing =>[ActivationCreated(35): rule=findSeating [fid:19:33]:[Seating id=3, pid=2, done=true, ls=2, ln=n4, rs=3, rn=n3] [fid:4:4]:[Guest name=n3, sex=m, hobbies=h3] [fid:3:3]:[Guest name=n2, sex=f, hobbies=h3], [fid:12:29]* =>[ActivationCreated(35): rule=findSeating [fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] [fid:5:5]:[Guest name=n4, sex=m, hobbies=h1] [fid:2:2]:[Guest name=n2, sex=f, hobbies=h1] =>[ActivationCreated(35): rule=findSeating [fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [fid:9:9]:[Guest name=n5, sex=f, hobbies=h1], [fid:1:1] : [Guest name=n1, sex=m, hobbies=h1] Assign Seating =>[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]] =>[fid:25:37]:[Path id=4, seat=4, guest=n2]] =>[fid:26:38]:[Chosen id=3, name=n2, hobbies=h3] ==>[ActivationCreated(40): rule=makePath [fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2] [fid:23:32]:[Path id=3, seat=2, guest=n4]* ==>[ActivationCreated(40): rule=makePath [fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2] [fid:20:27]:[Path id=3, seat=3, guest=n3]* =>[ActivationCreated(40): rule=makePath [fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2] [fid:22:31]:[Path id=3, seat=1, guest=n5]* =>[ActivationCreated(40): rule=done [fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]* Make Path =>fid:27:41:[Path id=4, seat=2, guest=n4] Make Path =>fid:28:42]:[Path id=4, seat=1, guest=n5]] Make Path =>fid:29:43]:[Path id=4, seat=3, guest=n3]] Path Done Continue Processing =>[ActivationCreated(46): rule=findSeating [fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] [fid:5:5]:[Guest name=n4, sex=m, hobbies=h1], [fid:2:2] [Guest name=n2, sex=f, hobbies=h1] =>[ActivationCreated(46): rule=findSeating [fid:24:44]:[Seating id=4, pid=3, done=true, ls=3, ln=n3, rs=4, rn=n2] [fid:2:2]:[Guest name=n2, sex=f, hobbies=h1] [fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]* =>[ActivationCreated(46): rule=findSeating [fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] [fid:9:9]:[Guest name=n5, sex=f, hobbies=h1] [fid:1:1]:[Guest name=n1, sex=m, hobbies=h1] Assign Seating =>[fid:30:47]:[Seating id=5, pid=4, done=false, ls=4, ln=n2, rs=5, rn=n1] =>[fid:31:48]:[Path id=5, seat=5, guest=n1] =>[fid:32:49]:[Chosen id=4, name=n1, hobbies=h1]
Miss Manners ベンチマーキングスクリプトの仕組みや、注意すべき警告や落とし穴について理解できたと思います。
8.10. ライフゲーム (Conway's Game Of Life) の例
| 名前: | Conway's Game Of Life |
| メインクラス: | org.drools.examples.conway.ConwayAgendaGroupRun org.drools.examples.conway.ConwayRuleFlowGroupRun |
| タイプ: | Java アプリケーション |
| Rules ファイル: | conway-ruleflow.drl conway-agendagroup.drl |
| 目的: |
accumulate、collect、および from を実証します。
|

図8.26 新しいゲームの開始
- 生きているセルに隣接する生きたセルが 1 つ以下の場合、孤独により死滅します。
- 生きているセルに隣接する生きたセルが 4 つ以上の場合、過密により死滅します。
- 死んでいるセルに隣接する生きたセルがちょうど 3 つある場合、死んでいるセルが生き返ります。

図8.27 進行中のゲーム
AgendaGroups (ConwayAgendaGroupRun) と RuleFlowGroups (ConwayRuleFlowGroupRun) による 2 つの方法を表しています。これら方法を比較することは大変有益です。本章では、読者のほとんどが使用するルールフローバージョンについて説明します。
session に挿入されます。ルールフロープロセスは、register neighbour と呼ばれるグループのルールに実行パーミッションを与えます。ルールグループは Neighbour Relation クラスを使用して、北東、北、北西、西に隣接するセルを登録します。この関係は両方向であるため、南方向のセルにルールを作成する必要がないことに注意してください。また制約により、セルが最後から 1 つ前の列と一番上から 1 つ前の行に置かれることにも注意してください。アクティベーションがすべて実行されるまでに、すべてのセルが隣接するすべてのセルに関係します。
例8.60 隣接するセルの関係をすべて登録
rule "register north east"
ruleflow-group "register neighbour"
when
CellGrid( $numberOfColumns : numberOfColumns )
$cell: Cell( $row : row > 0, $col : col <
( $numberOfColumns - 1 ) )
$northEast : Cell( row == ($row - 1), col == ( $col + 1 ) )
then
insert( new Neighbor( $cell, $northEast ) );
insert( new Neighbor( $northEast, $cell ) );
end
rule "register north"
ruleflow-group "register neighbour"
when
$cell: Cell( $row : row > 0, $col : col )
$north : Cell( row == ($row - 1), col == $col )
then
insert( new Neighbor( $cell, $north ) );
insert( new Neighbor( $north, $cell ) );
end
rule "register north west"
ruleflow-group "register neighbour"
when
$cell: Cell( $row : row > 0, $col : col > 0 )
$northWest : Cell( row == ($row - 1), col == ( $col - 1 ) )
then
insert( new Neighbor( $cell, $northWest ) );
insert( new Neighbor( $northWest, $cell ) );
end
rule "register west"
ruleflow-group "register neighbour"
when
$cell: Cell( $row : row >= 0, $col : col > 0 )
$west : Cell( row == $row, col == ( $col - 1 ) )
then
insert( new Neighbor( $cell, $west ) );
insert( new Neighbor( $west, $cell ) );
end
Generation ルールフローが実行されます。このルールフローは繰り返しサイクルごとにセルの変更をすべて管理します。

図8.28 生成ルールフロー
evaluate グループより実行されます。その結果、このグループのアクティブなルールはすべて実行可能になります (このグループのルールは、本項の最初に説明したメイン Game ルールを適用します。これらのルールは、死滅するセルと生き返るセルを決定します)。
RuleFlowGroup の 1 つと関係します。
例8.61 ステート変更でセルを評価
rule "Kill The Lonely"
ruleflow-group "evaluate"
no-loop
when
# A live cell has fewer than 2 live neighbors
theCell: Cell(liveNeighbors < 2, cellState ==
CellState.LIVE, phase == Phase.EVALUATE)then
theCell.setPhase(Phase.KILL);
update( theCell );
end
rule "Kill The Overcrowded"
ruleflow-group "evaluate"
no-loop
when
# A live cell has more than 3 live neighbors
theCell: Cell(liveNeighbors >; 3, cellState ==
CellState.LIVE, phase == Phase.EVALUATE)then
theCell.setPhase(Phase.KILL);
update( theCell );
end
rule "Give Birth"
ruleflow-group "evaluate"
no-loop
when
# A dead cell has 3 live neighbors
theCell: Cell(liveNeighbors == 3, cellState ==
CellState.DEAD, phase == Phase.EVALUATE)then
theCell.setPhase(Phase.BIRTH);
update( theCell );
end
reset calculate ルールは、以前のデータ変更によって発生した計算アクティベーションを calculate グループから消去します。次に、「split」が入力されます。これにより、「kill」および「birth」グループの両方にあるアクティベーションを実行できます (これらのルールは状態の変更を適用します)。
例8.62 変更の適用
rule "reset calculate"
ruleflow-group "reset calculate"
when
then
WorkingMemory wm = drools.getWorkingMemory();
wm.clearRuleFlowGroup( "calculate" );
end
rule "kill"
ruleflow-group "kill"
no-loop
when
theCell: Cell( phase == Phase.KILL )
then
modify( theCell ){
setCellState( CellState.DEAD ),
setPhase( Phase.DONE );
}
end
rule "birth"
ruleflow-group "birth"
no-loop
when
theCell: Cell( phase == Phase.BIRTH )
then
modify( theCell ){
setCellState( CellState.LIVE ),
setPhase( Phase.DONE );
}
neighbour relation が使用されるため、生きている隣接するセルの数が増加または減少します。セルの数が変わったセルはすべて evaluate フェーズに設定され、ルールフロープロセスの次の段階で評価されます。
evaluate がクリックされた場合はエンジンがルールフロープロセスを再度実行します。
例8.63 ステート変更があったセルの評価
rule "Calculate Live"
ruleflow-group "calculate"
lock-on-active
when
theCell: Cell( cellState == CellState.LIVE )
Neighbor( cell == theCell, $neighbor : neighbor )
then
modify( $neighbor ){
setLiveNeighbors( $neighbor.getLiveNeighbors() + 1 ),
setPhase( Phase.EVALUATE );
}
end
rule "Calculate Dead"
ruleflow-group "calculate"
lock-on-active
when
theCell: Cell( cellState == CellState.DEAD )
Neighbor( cell == theCell, $neighbor : neighbor )
then
modify( $neighbor ){
setLiveNeighbors( $neighbor.getLiveNeighbors() - 1 ),
setPhase( Phase.EVALUATE );
}
end

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.