Tue, January 26, 2010
条件判定にクロージャを利用 groovy
たとえば、無数の点が平面上に存在していて、 その点と点の関係を調べてグループ分けしたい、という問題を解く場合。
考えられるグループは...
- 近くにある点のグループ
- 縦に揃っている点グループ
- 横に揃っている点グループ
- ある図形の上に存在している点のグループ(星座のように)
などが考えられます。
点に対するグループの分け方ルールは無数に存在するので、 思いつきでどんどんルールを追加・変更できるように設計しておきたい。
こういう場合に、普通のJava的発想では、 点同士の関係を解析する IAnalyzer インターフェスを定義して... という発想になってしまうのですが、 GroovyのClosureを使えばもっとスマートに速く問題を解決できそうです。
Closureでルールを定義していく
コード mypoint.groovy
class MyPoint extends java.awt.Point {
def と次の点を調べる( MyPoint pt, Closure c ){ c.call(this,pt) }
}
def pt0 = new MyPoint(x:10,y:10)
def pt1 = new MyPoint(x:10,y:12)
// テスト1
println '横に並んでいるいるか? -> '+ pt0.と次の点を調べる(pt1){ p0,p1-> ( p0.y == p1.y && p0.x!=p1.x ) }
// テスト2
def すぐ下にあるか = { p0,p1->
(p0.x == p1.x && Math.abs(p0.y-p0.y)<5)
}
println 'すぐ下にあるか? -> ' + pt0.と次の点を調べる(pt1,すぐ下にあるか)
実行
$ groovy mypoint.groovy
横に並んでいるいるか? -> false
すぐ下にあるか? -> true
説明
MyPoint という点(x,y)を保持したクラスを用意し、
そこに、と次の点を調べる という MyPointとClosureを引数に持つメソッドを定義するだけ。
あとは、groovy スクリプトで、点と点に関するルールを書いたクロージャを
思いついた段階でどんどん書いていけばよい。
興味深いのはコード自体が処理内容の説明になっている点。
たとえば、MyPoint のインスタンス point1 と point2 が完全に重なった点かどうかを調べるには...
def 結果 = point1.と次の点を調べる(point2) { p1,p2-> (p1.x==p2.x && p1.y==p2.y) }
と書けばよい。
もし、この重なった点かどうかを調べるクロージャを再利用したいならば...
def 2つの点は重なっているか = {p1,p2-> (p1.x==p2.x && p1.y==p2.y) }
と定義しておいてから、
def 結果 = point1.と次の点を調べる( point2, 2つの点は重なっているか )
println 結果
とすればよい。プログラムがそのまま自分自身の処理の説明になっている点がすごい。