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 Process Automation 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" の出力
 
    