Drools 4.0 入門3 (サンプルプログラム2)

最大値を求めるサンプルプログラム

次は、Drools 4.0 の宣言的プログラミングの性質を表すサンプルプログラムを紹介します。
(ちなみに以下の記事はJBoss Rulesサンプルプログラム3をDrools 4.0に合わせて修正したものです。内容的にはほぼ同じですが、ルールの記述でDrools 4.0で追加された新たな述語forallを使っています)

  「宣言的」プログラミングとは、結果を「どのようにして(how)」求めるかを記述するのでなく、結果が「何であるか(what)」を記述することでプロ グラムを作成していく方法です。 たとえば、最大値を求めるプログラムでは、通常の手続き型(命令型)のプログラム言語で実装する場合、値の集合をすべて チェックするようなループを使ってそれぞれの値を比較し、すべてのチェックが終り、最終的に最大値が得られるというようなプログラムを書くでしょう。ここ で、最大値の言葉の定義に立ち返ってみましょう。最大値とは何らかの値の集合の中で一番大きい値です。言い換えると、
  「ある値の集合の中に、その値よりも大きな値が存在しない値」のこと
となります。さて、宣言的プログラミングでは、最大値を求めるプログラムの場合、上記のような最大値の「定義」をそのままプログラムに落とし込みます。

 では、具体的なプログラムを見てみましょう。以下は総務課のメンバー中で最も身長の高い人を選び出すプログラムです。まずは、総務課の個々のメンバーを表すクラスを定義します。

Member.java

package com.sample;

public class Member {
 private String name;
 private String section;
 private int height;

 public Member(String name, String section, int height) {
  super();
  this.name = name;
  this.section = section;
  this.height = height;
 }

 public String getSection() {
  return section;
 }

 public void setSection(String name) {
  this.section = name;
 }

 public int getHeight() {
  return height;
 }

 public void setHeight(int height) {
  this.height = height;
 }
 
 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

}

 

さらに、メインプログラムは、以下のとおりです。

DroolsTest.java

package com.sample;

import java.io.InputStreamReader;
import java.io.Reader;

import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.WorkingMemory;
import org.drools.compiler.PackageBuilder;
import org.drools.rule.Package;

public class DroolsTest {

    public static final void main(String[] args) {
        try {
         
         //load up the rulebase
            RuleBase ruleBase = readRule();
            WorkingMemory workingMemory = ruleBase.newStatefulSession();
                   
            Member memFact1 = new Member(“友里”,”総務”,168);
            workingMemory.insert( memFact1 );
            Member memFact2 = new Member(“優”,”総務”,170);
            workingMemory.insert( memFact2 );
            Member memFact3 = new Member(“もえ”,”総務”,168);
            workingMemory.insert( memFact3 );
            Member memFact4 = new Member(“茉希”,”総務”,168);
            workingMemory.insert( memFact4 );
            Member memFact5 = new Member(“紗世”,”総務”,165);
            workingMemory.insert( memFact5 );

            workingMemory.fireAllRules();            
           
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

private static RuleBase readRule() throws Exception {
  Reader source = new InputStreamReader( DroolsTest.class.getResourceAsStream( “/Sample.drl” ) );
  
  PackageBuilder builder = new PackageBuilder();

  builder.addPackageFromDrl( source );

  Package pkg = builder.getPackage();
  
  RuleBase ruleBase = RuleBaseFactory.newRuleBase();
  ruleBase.addPackage( pkg );
  return ruleBase;
 }
   
}

ここまでは、あまり説明の必要はないと思いますが念のため、mainのプログラムでworkingMemory.insertというところで、メンバーのファクトをワーキングメモリーに登録していきます。

最後に肝心なルールのソースです。

sample.drl

package com.sample
 
import com.sample.Member;

rule “maxHeight”
  when
    memMaxHeight: Member( section == “総務”, maxheight : height )
    forall ( Member( section == “総務”, height <= maxheight))
  then
    System.out.println(“最も背が高い人の身長は、”+maxheight+”cm”);
    System.out.println(“一番背が高いのは ”
         +memMaxHeight.getName()
         +”で身長”
         +memMaxHeight.getHeight()
         +”cm”);
end

 

when~then の~の部分を見てください。まずwhen節の2行目は、「すべての総務課の人はmaxheight以下の身長である。 」ということを表しています。1行 目はそれのみであれば、総務のメンバー全員にマッチしますが、2行目と組み合わせることで、総務のメンバーはみな自分の身長以下であるということを表すことになります。

 さて、これを実行すると

最も背が高い人の身長は、170cm
一番背が高いのは 優で身長170cm

という結果が表示されます。ワーキングメモリに登録する総務課のメンバーを追加したり替えてみていろいろとやってみてください。

(おまけの参考)上記ルールの論理的な意味

述語論理をちょっとかじったことのあるひとは、forallが∀をあらわしていることにすぐ気がつくとおもいます。

  E:会社の従業員の集合
  G:総務課の従業員の集合
  H(e):従業員の身長

とすると、 e ∈ E として、maxheightが総務課の従業員の身長の最大値である・・・すなわち

    maxheight = max(H(e);e ∈ G)

であるとは、定義によって以下と同値。

    ∀e[(e ∈ G) かつ (maxheight ≧ H(e))]

これは、ルールファイルのwhen節の2行目と同じです。
 ・・・このように定義に立ち戻って問題そのものを厳密に記述していくことが宣言型のプログラミングの特徴です。