倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Closure クラスの API を使ってみる (4) : クロージャの合成 leftShift(), rightShift()

前回までで Groovy 1.7 以前に Closure クラスに定義されていたメソッドは大体使ってみました(幾つか残ってますが)。 1.8から追加されたメソッドは『プログラミングGROOVY』で解説されてるし、ネット上でも記事を見つけられるので、今まで以上に雑な説明で Go。 今回はクロージャを関数として合成するメソッドです。

参考 URL

クロージャを関数として合成するメソッド


クロージャの合成を行うメソッドは leftShift(), rightShift() メソッドの2つがあり、どちらのクロージャが先に適用されるかが異なります。 Object を引数としてとる leftShift() メソッドは合成とは関係なく、クロージャを実行します。

class Closure<V>{
    // 合成
    Closure<V> leftShift(Closure other)
    <W> Closure<W> rightShift(Closure<W> other)

    // 実行
    V leftShift(Object arg)

Groovy コード上では、通常これらのメソッドを直接呼び出さず、それぞれ「<<」と「>>」演算子によって実行します。 実行時にクロージャが適用される順番は

  • f << g ・・・> f(g(...))
  • f >> g ・・・> g(f(...))

となります。 数学の合成関数と順番があってるのは「<<」の方です。

Closure クラスは返り値の型を Generics の型パラメータとして指定できるので、合成に関するメソッドも型パラメータがきちんと定められます。

  • f << g では返り値は(後で適用される) f と同じ方なので Closure<V> が返される
  • f >> g では返り値は g と同じ型なので Closure<W> が返される(W は引数のクロージャの型パラメータに指定されている型)

まぁ、そんなに気にする必要は生じませんが。

サンプル・コード


では、サンプル・コード。 数学関数の合成みたいなサンプルはよく見るので、ここでは3つの要素をもつ List を使って置換を行うサンプルをやってみます:

// 要素の置換を行うクロージャ
def f = { x -> x[2, 1, 0] }
def g = { x -> x[1, 0, 2] }

def x = ['a', 'b', 'c']
// 以下のように置換が実行される
assert f(x) == ['c', 'b', 'a']
assert g(x) == ['b', 'a', 'c']

//***** 合成
def p = f << g    // leftShift
assert p(x) == f(g(x))    // ['b', 'c', 'a']

def q = f >> g    // rightShift
assert q(x) == g(f(x))    // ['c', 'a', 'b']

//***** 短く書くと・・・
assert (f << g)(x) == f(g(x))    // ['b', 'c', 'a']
assert (f >> g)(x) == g(f(x))    // ['c', 'a', 'b']

//***** leftShift(Object) は合成ではない
assert (f << x) == f(x)
assert (g << x) == g(x)

まぁ、こんな感じで。

プログラミングGROOVY

プログラミングGROOVY