1.3.1.プロダクションルール
Drools(JBoss Rules)において、プロダクションルール、または単にルールは、レフトハンドサイド(LHS)とライトハンドサイド(RHS)の二つの部分を持ったつくりになっています。それに加えルールは以下にあげる属性を持つこともできます。
-
salience
-
agenda-group
-
auto-focus
-
activation-group
-
no-loop
-
duration
rule “<名前>”
<属性> <値>
when
<LHS>
then
<RHS>
end
ルールのLHSは、条件要素(Conditional Element)とカラム(Column)からなっており、命題論理や1階j述語論理のコーディングをやりやすくしています。カラムという用語は、ファクトに対するフィールド制約を表すために使われます。
Droolsは、現在のところ次の条件要素をサポートしています。
-
‘and’
-
‘or’,
-
‘not’,
-
‘exists’
まもなく、’forall’ や ‘accumulate’がこれに加わるでしょう。
以下は、許されているフィールド制約です。
リテラル制約(Literal Constraint)
境界変数制約(Bound Variable Constraint)
戻り値(Return Value)
述語(Predicate)
これらについては言語ガイドの章で、より詳しい説明を得られるでしょう。
ワーキングメモリの中でファクトが宣言され、修正されるにしたがって、LHSの条件に対してファクトをマッチさせます。すべての条件がマッチし、かつ真であるとき、ルールとそのルールにマッチしたファクトの組が活性化(activate)します。ルールが活性化したとき、それは実行の候補としてアジェンダに加えられ、そこでRHSのアクション-結果(Consequence)と呼ばれる-が実行されることになります。LHSとRHSは、
if ( <LHS> ) {
<RHS>
}
に似ています。
しかし、’if’は、if then…. else if….else….といったような実行フローの一部分であるという意味からすると手続き的です。ルールでは、ルールのLHSがマッチした「とき」そしてその「とき」のみ活性化されるということの意味をより明確に示すため ’when’という言葉を用います。
ルールは、packageキーワードによる名前空間に関連付けられています。他のルールエンジンでは、Rule Setなどと呼ばれることもあるかもしれません。パッケージは、インポートimport、大域変数global variables、関数function、ルールruleを宣言します。
package com.sample
import java.util.List
import com.sample.Cheese
global List cheeses
function void exampleFunction(Cheese cheese) {
System.out.println( cheese );
}
rule “A Cheesey Rule”
when
cheese : Cheese( type == “stilton” )
then
exampleFunction( cheese );
cheeses.add( cheese );
end
次は、チーズ工場について、ひとつのリテラルフィールド制約を持ったひとつのカラムで構成されているLHSの例を表しています。
rule "Cheddar Cheese"
when
Cheese( type == "cheddar" )
then
System.out.println( "cheddar" );
end
上の例は、次と似ています。
public void cheddarCheese(Cheese cheese) {
if ( cheese.getType().equals("cheddar") {
System.out.println( "cheddar" );
}
}
ルールは、データからロジックを完全に切り離したものです。ルールは、メソッドや関数と違い直接呼び出されるわけではなく、ワーキングメモリのデータが変更されることに応じて発火します。またルールは、Javaに代表される手続き型言語のように「どのように」行うかを記述するのではなく、「何を」行うかを記述するという点で完全に宣言的なものです。
1.3.2. 1階述語論理
ルールは1階述語論理(あるいは単に述語論理)-命題論理を拡張した論理-で記述されます。Emil Leon Postは、シンボルを用いて論理を表現する推論システムを最初に開発した人物であり、その結果、どのような論理体系(数学を含む)も、そういったシステムで表現できることを証明できました。
命題は、真であるか偽であるか決定できる文のことを言います。もし真偽がその文単独で決定できるなら、その文を「閉じた文」と言います。プログラミング用語におきかえると、このことは変数を含んでいない式のことを表しています:
10 = = 2 * 5
ひとつまたは複数の変数、ファクトに対しての評価を含む式は「開いた文」です。ここでは、変数の値が定まらない限りは、その文が真かどうかを決めることはできません。:
Person.sex == “male”
ユーザに上の文にマッチするファクトを返す結論を導くアクションは、SQLで表せます:
select * from People where People.sex == “male”
結果として得られるすべての行-これは我々が言うところのファクトを表しますが-について、それらのファクトは、男性であるということを推論しています。次の図は、上記のSQLステートメントとPeopleテーブルが推論エンジンの用語によって、どのように表現されるかを表しています。
javaでは、簡単な命題は、変数、演算子、値のフォームであるということが言えます。-我々はよく値をリテラルと称します-命題はフィールド制約として考えられるのです。さらに命題は、連言(かつ)や選言(または)結合子-論理学者は’&&’や’||’と表現します-で結びつけることもできます。次はひとつの選言結合子で一緒に結び付けられた2つの開いた命題の文を表しています。
person.getEyeColor().equals("blue") || person.getEyeColor().equals("green")
これは、選言の条件要素結合子を使って表すこともできます。これは実際のところ2つのルールが生成した結果になり、2つの論理的帰結を表現しています。
Person( eyeColour == "blue" ) || Person( eyeColor == "green" )
選言のフィールド制約を用いることもできます-ただし、現在のところDrools3.0ではサポートされていません-が、複数のルールが生成した結果ではありません。
Person( eyeColour == "blue"||"green" )
しかしながら、命題論理はチューリング完全ではありません。データ構造を評価する表現ができないので、定義できる問題が限られているからです。1階述語論理または単に述語論理は命題論理の拡張で、構造を定義する2つの量化子が新たに導入されます。全称量化子(∀)と存在量化子(∃)です。全称量化子は、すべてに対してある文が真であるということを判定できるようにします。通常’forall’条件要素としてサポートされますが、Drools3.0では今のところ実装されていません。存在量化子は、ある文に対して少なくともひとつ存在するということを判定できるようにします。これは、’not’や’exists’条件要素でサポートされています。
StudentとModuleの2つのクラスを考えて見ましょう。Moduleは、ある学生がその学期に出席するそれぞれのコースを表し、Listで表されます。学期の終りにはそれぞれのモジュールに点数がつきます。学生は、点数が40点未満であるようなModuleが一つでもあると、その学期を落とします。上記の評価基準に対して、モジュールの存在が真であるかを判定するためには、「40点未満」であるという開いた命題に存在量化子を用いることで表現できます。
public class Student {
private String name;
private List modules;
...
}
public Class Module {
private String name;
private String studentName;
private int score;
Javaは、存在を判定するためにデータ構造を反復生成できるコードを書けるので、チューリング完全です。次は、その学期を落とした学生のリストを返します。
List failedStudents = new ArrayList();
for ( Iterator studentIter = students.iterator(); studentIter.hasNext() {
Student student = ( Student ) studentIter.next();
for ( Iterator it = student.getModules.iterator(); it.hasNext(); ) {
Module module = ( Module ) it.next();
if ( module.getScore() < 40 ) {
failedStudents.add( student ) ;
break;
}
}
}
初期のSQLの実装は、データ構造を評価する量化子を持たなかったため、チューリング完全ではありません。しかしながら、最近のSQLエンジンは’exists’や’in’のようなキーワードで結び付けられるような入れ子になったSQLも使えます。次にあげるクエリーは、その学期を落とした学生の集合を返すでしょう。
select
*
from
Students s
where exists (
select
*
from
Modules m
where
m.student_name = s.name and
m.score < 40
)
rule
when
exists( $student : Student() && Module( student == $student, score < 40 ) )