21.10. House of Doom 例のデシジョン (後向き連鎖および再帰)
House of Doom のデシジョンセットの例では、デシジョンエンジンが後向き連鎖と再帰を使用して、階層システムで定義した目的やサブゴールに到達する方法を説明します。
以下は House of Doom の例の概要です。
-
名前:
backwardchaining
-
Main クラス: (
src/main/java
内の)org.drools.examples.backwardchaining.HouseOfDoomMain
-
モジュール:
drools-examples
- タイプ: Java アプリケーション
-
ルールファイル: (
src/main/resources
内の)org.drools.examples.backwardchaining.BC-Example.drl
- 目的: 後向き連鎖と再帰を例示します。
後向き連鎖のルールシステムは、通常再帰を使用して、デシジョンエンジンが満たそうとする結論から開始する目的駆動型のシステムです。システムが結論または目的に到達できない場合は、サブとなる目的、つまり、現在の目的の一部を完了する結論を検索します。システムは、最初の結論が満たされるか、すべてのサブとなる目的が満たされるまでこのプロセスを続行します。
反対に、前向き連鎖のルールシステムは、デシジョンエンジンのワーキングメモリーにあるファクトで開始して、そのファクトへの変更に反応するデータ駆動型のシステムです。オブジェクトがワーキングメモリーに挿入されると、その変更の結果として True となるルールの条件はすべて、アジェンダによって実行されるようにスケジュールされます。
Red Hat Decision Manager のデシジョンエンジンは、前向き連鎖と後向き連鎖の両方を使用してルールを評価します。
以下の図は、デシジョンエンジンが、ロジックフローで後向き連鎖のセグメントと、前向き連鎖全体を使用してルールを評価する方法を例示します。
図21.27 前向き連鎖と後向き連鎖を使用したルール評価のロジック
House of Doom の例は、さまざまなクエリータイプが含まれるルールを使用し、部屋の場所と家の中のアイテムを探し出します。Location.java
のサンプルクラスには、この例で使用する item
と location
要素が含まれます。HouseOfDoomMain.java
のサンプルクラスで、家の該当の場所にアイテムまたは部屋を挿入して、ルールを実行します。
HouseOfDoomMain.java クラスでのアイテムと場所
ルールの例では、家の構造の中で全アイテムおよび部屋の場所を判断するのに、後向き連鎖と再帰を使用します。
以下の図は、House of Doom の構造と、その構造内のアイテムと部屋を示しています。
図21.28 House of Doom の構造
この例を実行するには、IDE で Java アプリケーションとして org.drools.examples.backwardchaining.HouseOfDoomMain
クラスを実行します。
実行後に、以下の出力が IDE コンソールウィンドウに表示されます。
IDE コンソールでの実行出力
この例のルールはすべて実行し、家の中の全アイテムの場所を検出して、出力でそれぞれの場所を出力します。
再帰クエリーおよび関連のルール
再帰クエリーは、要素間の関係におけるデータ構造階層を使用して繰り返し検索を行います。
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
query isContainedIn( String x, String y )
Location( x, y; )
or
( Location( z, y; ) and isContainedIn( x, z; ) )
end
"go"
のルールは、システムに挿入する文字列をすべて出力し、アイテムをどのように導入し、"go1"
ルールが isContainedIn
クエリーを呼び出すかを判断します。
ルール "go" および "go1"
この例は、"go1"
文字列をデシジョンエンジンに挿入して、"go1"
ルールを有効化し、House
の場所にある Office
アイテムを検出します。
文字列の挿入とルールの実行
ksession.insert( "go1" ); ksession.fireAllRules();
ksession.insert( "go1" );
ksession.fireAllRules();
IDE コンソールでのルール "go1" の出力
go1 Office is in the House
go1
Office is in the House
推移閉包ルール
推移閉包は、階層構造の複数レベルであり、上層にある親要素に含まれる要素間の関係です。
"go2"
ルールは、Drawer
と House
の推移閉包の関係を特定します。Drawer
は、House
の中の、Office
の中の、Desk
の中にあります。
この例は、"go2"
文字列をデシジョンエンジンに挿入して、"go2"
ルールを有効化し、最終的に House
の場所に含まれる Drawer
アイテムを検出します。
文字列の挿入とルールの実行
ksession.insert( "go2" ); ksession.fireAllRules();
ksession.insert( "go2" );
ksession.fireAllRules();
IDE コンソールのルール "go2" の出力
go2 Drawer is in the House
go2
Drawer is in the House
デシジョンエンジンは、以下のロジックをもとにこの結果を判断します。
-
クエリーは再帰的に、家の中の複数レベルを検索して、
Drawer
とHouse
の間の推移閉包を検出します。 -
Drawer
はHouse
に直接含まれないため、Location( x, y; )
を使用する代わりに、このクエリーは(z, y; )
の値を使用します。 -
z
の引数は現在バインドされておらず、値が指定されていないため、引数に含まれるものはすべて返されます。 -
y
の引数は現在、House
にバインドされているため、z
はOffice
とKitchen
を返します。 -
クエリーは、
Office
からの情報を収集して、Drawer
がOffice
に含まれているかを再帰的にチェックします。これらのパラメーターに対して、クエリーの行isContainedIn( x, z; )
が呼び出されます。 -
Office
に直接含まれるDrawer
が存在しないため、一致するものはありません。 z
のバインドがない場合、このクエリーではOffice
内のデータが返され、z == Desk と判断されます。isContainedIn(x==drawer, z==desk)
isContainedIn(x==drawer, z==desk)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow isContainedIn
クエリーは再帰的に 3 回検索し、3 回目に、このクエリーによりDesk
の中にDrawer
があることが検出されます。Location(x==drawer, y==desk)
Location(x==drawer, y==desk)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
最初の場所で上記が一致した後に、このクエリーにより再帰的に構造を上方向に検索し、
Drawer
がDesk
の中に、Desk
がOffice
の中に、Office
がHouse
の中にあることを判断します。このように、Drawer
はHouse
の中にあるため、このルールは満たされます。
リアクティブクエリールール
リアクティブクエリーでは、データ構造の階層を検索して、要素間に関係があるかを確認し、構造内の要素が変更されると動的に更新されます。
"go3"
ルールは、リアクティブクエリーとして機能し、推移閉包により、新しいアイテム Key
が Office
に含まれるかどうかを検出します (Office
の中の Drawer
の中の Key
など)。
ルール "go3"
この例は、"go3"
文字列をデシジョンエンジンに挿入して、"go3"
ルールを有効にします。最初は、Key
が家の構造に存在するため、このルールは満たされず、出力が生成されません。
文字列の挿入とルールの実行
ksession.insert( "go3" ); ksession.fireAllRules();
ksession.insert( "go3" );
ksession.fireAllRules();
IDE コンソールのルール "go3" の出力 (条件を満たさない)
go3
go3
この例では、Office
の中にある Drawer
の場所に、新しいアイテム Key
を挿入します。この変更で、"go3"
ルールの推移閉包が満たされ、それに合わせて出力が生成されます。
新規アイテムの場所の挿入とルールの実行
ksession.insert( new Location("Key", "Drawer") ); ksession.fireAllRules();
ksession.insert( new Location("Key", "Drawer") );
ksession.fireAllRules();
IDE コンソールのルール "go3" の出力 (条件を満たす)
Key is in the Office
Key is in the Office
またこの変更で、クエリーにより、後に続く再帰検索に含まれるよう、この構造に別のレベルが追加されます。
ルールにバインドなしの引数が含まれたクエリー
バインドなしの引数が 1 つ以上あるクエリーでは、クエリーの定義済み (バインドされている) 引数に含まれる未定義 (バインドされていない) のアイテムをすべて返します。クエリー内の引数でバインドされているものがない場合、クエリーはクエリーの範囲内のアイテムをすべて返します。
"go4"
ルールは、バインドされている引数を使用して、Office
内の特定のアイテムを検索するのではなく、バインドされていない引数 thing
を使用して、バインドされている引数 Office
内の全アイテムを検索します。
ルール "go4"
この例では "go4"
文字列をデシジョンエンジンに挿入して、"go4"
ルールをアクティベートし、Office
の全アイテムを返します。
文字列の挿入とルールの実行
ksession.insert( "go4" ); ksession.fireAllRules();
ksession.insert( "go4" );
ksession.fireAllRules();
IDE コンソールのルール "go4" の出力
"go5"
ルールは、バインドされていない引数 thing
と location
を使用して、House
の全データ構造の中に含まれる全アイテムとその場所を検索します。
ルール "go5"
この例は "go5"
文字列をデシジョンエンジンに挿入して、"go5"
ルールをアクティベートし、House
データ構造に含まれる全アイテムとその場所を返します。
文字列の挿入とルールの実行
ksession.insert( "go5" ); ksession.fireAllRules();
ksession.insert( "go5" );
ksession.fireAllRules();
IDE コンソールのルール "go5" の出力