No Programming, No Life

プログラミング関連の話題や雑記

Groovyでクロージャを期待するメソッド呼び出し時の挙動について

はじめに

(10..1e4).grep(~(9..0).sum{"$it?"}).any''.&println

anarchy golf - the source code

を見ていたら見つけたのですが、Groovyってクロージャを引数として期待しているメソッドにクロージャインスタンスを渡した場合は自動でうまい感じに動くんですね。
文章で書くよりも例を見た方が分かりやすいと思うので、下をどうぞ。

例1:1〜10までprintする

(1..10).each(Object.&print)
結果


12345678910

例2:文字列終端判断

['ga', 'gi', 'gu', 'ge', 'go'].collect('hoge'.&endsWith)
結果


[false, false, false, true, false]

例3:文字列インデックス判断

def fox = 'The quick brown fox jumps over the lazy dog'
def methodClos = fox.&indexOf
def indexes = ('a'..'z').collect(methodClos)
assert indexes.size() == 26 // it's size of all alphabets!!
println indexes
結果 (indexesの内容)


[36, 10, 7, 40, 2, 16, 42, 1, 6, 20, 8, 35, 22, 14, 12, 23, 4, 11, 24, 31, 5, 27, 13, 18, 38, 37]

例4:クロージャを呼び出す引数の数がまちまちの場合

// クロージャの定義
def tr = 'abc123'.&tr
def concat2 = { a, b -> a*2 + b*2 }
def keyValue = { "key=${it.key} value=${it.value}" }

// マップを定義
def map = [
  'a-c' : 'A-C',
  '1-9' : '9-1'
]

// テスト
assert map.collect(tr) == ['ABC123', 'abc987']
assert map.collect(concat2) == ['a-ca-cA-CA-C', '1-91-99-19-1']
assert map.collect(keyValue) == ['key=a-c value=A-C', 'key=1-9 value=9-1']

分かったこと

要するに、クロージャが期待する引数の数と、クロージャを期待するメソッドが内部でクロージャを呼び出す際に指定している引数の数が合っていれば自動でそこの引数に値を入れてクロージャを呼び出してくれるようだ。

なので

(1..10).each{ print it }

def c = { print it } // { a -> print a } でも可
(1..10).each(c)

def c2 = Object.&print // => print = { print it } のことですよね
(1..10).each(c2)

は、どれも一緒になるってことですね。

最後にちょっと応用例

応用例ということで、カリー化して引数の数を調整してから渡してみます。

// 右からカリー化してエンコードを指定する
// see. URLEncoder#encode(String str, String encode)
// すると、encは文字列を一つ期待するクロージャとなる
def enc = URLEncoder.&encode.rcurry('UTF-8')
assert 'あいう'.collect(enc) == ['%E3%81%82', '%E3%81%84', '%E3%81%86']