あまり使ったことがないので、メモを兼ねて試してみます。
Groovy
Groovyの場合は、Closureクラスのインスタンスを合成します。使用するメソッドは、leftShift(<<)とrightShift(>>)です。
def add2 = { x -> x + 2 } def multiply3 = { x -> x * 3 } def multiply3AndAdd2 = add2 << multiply3 // f << g は f(g(x)) def add2AndMultiply3 = add2 >> multiply3 // f >> g は g(f(x)) println(multiply3AndAdd2(2)) // (3 * 2) + 2 => 8 println(add2AndMultiply3(2)) // (2 + 2) * 3 => 12
f << g
でf(g(x))なので、gの結果をfに適用するよ!
f >> g
でg(f(x))なので、fの結果をgに適用するよ!と覚えればいいかな?
合成できるのはあくまでClosureクラスのインスタンスなので、メソッドを合成したい場合は1度Closureクラスに変換してあげる必要があります。
def add2Method(x) { x + 2 } def multiply3Method(x) { x * 3 } def multiply3AndAdd2ByMethod = this.&add2Method << this.&multiply3Method def add2AndMultiply3ByMethod = this.&add2Method >> this.&multiply3Method println(multiply3AndAdd2ByMethod(2)) // => 8 println(add2AndMultiply3ByMethod(2)) // => 12
「.&」演算子でメソッドをClosureに変換できるそうなのですが、単独で使ってもダメで必ずレシーバーが必要なようです…。また、「&」演算子なわけでもありません。つまり、このような記法や
def multiply3AndAdd2ByMethod = .&add2Method << .&multiply3Method def add2AndMultiply3ByMethod = .&add2Method >> .&multiply3Method
このような記法は、共にNGです。
def multiply3AndAdd2ByMethod = &add2Method << &multiply3Method def add2AndMultiply3ByMethod = &add2Method >> &multiply3Method
トップレベルのメソッドに使用したい場合は、thisを付けてってことですね。
Scala
Scalaの場合は、Function1トレイトのインスタンスを合成します。
val add2 = (x: Int) => x + 2 val multiply3 = (x: Int) => x * 3 val multiply3AndAdd2 = add2 compose multiply3 // f compose g は f(g(x)) val add2AndMultiply3 = add2 andThen multiply3 // f andThen g は g(f(x)) println(multiply3AndAdd2(2)) // (3 * 2) + 2 => 8 println(add2AndMultiply3(2)) // (2 + 2) * 3 => 12
andThenがあるので、逆がcomposeと最初は見ればいい…?ま、慣れれば迷わなくなりますね。
メソッドを合成する場合は、1度Function1トレイトのインスタンスに変換してあげる必要があります。
def add2Method(x: Int): Int = x + 2 def multiply3Method(x: Int): Int = x * 3 val multiply3AndAdd2ByMethod = add2Method _ compose multiply3Method val add2AndMultiply3ByMethod = add2Method _ andThen multiply3Method println(multiply3AndAdd2ByMethod(2)) // => 8 println(add2AndMultiply3ByMethod(2)) // => 12
「_」を付けて、部分適用せい、と。
Clojure
Clojureは、そのまま関数定義を合成できます。
(defn add2 [x] (+ x 2)) (defn multiply3 [x] (* x 3)) (def multiply3AndAdd2 (comp add2 multiply3)) ;; comp f g は (f (g x)) (println (multiply3AndAdd2 2)) ;; (+ (* 2 3) 2) => 8
もちろん、無名関数から合成しても問題ありません。
(def f #(+ % 2)) (def g #(* % 3)) (println ((comp f g) 2)) ;; => 8
まあ、defnってマクロらしいから、意味一緒なのかな…。
ただ、g(f(x))となるような関数合成を行う関数は、標準にはないみたいですね。