カリー化ってなに?
複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること
サンプル(Javascript)
var func = function(x, y) { return x * y; }
var curried = function(x) {
return function(y) {
return func(x, y);
}
}
curried(2)(3); // 6
部分適用とカリー化の違い
カリー化は部分適用と混同されやすい。部分適用とは、複数引数ある関数の引数の一部だけに実引数を適用する操作のこと
一方、カリー化は、引数への値の適用までは行わない
サンプル(Javascript)
var func = function(x, y) { return x * y; }
// 部分適用の為の関数(curried)を作る所までがカリー化
var curried = function(x) {
return function(y) {
return func(x, y);
}
}
// カリー化された関数を使って、部分適用された関数(partialized)を作る
var partialized = curried(2);
partialized(3); // 6
Ruby でカリー化
pr = -> x, y { x * y } # Proc.new のシンタックスシュガー
puts pr.(2,3) # -> 6 : .() は、Proc#call のシンタックスシュガー
# Proc インスタンスのカリー化
pr_crrried = pr.curry
# Proc インスタンスの部分適用
pr_partialized = pr_crrried.(2)
puts pr_partialized.(3) # -> 6
カリー化すると何が得なの?
複数の引数を持つ関数に予め引数を渡しておける
複数の引数を持つ関数をmapしたい場合
pr = -> x, y { x * y }
p [1, 2, 3].map {|x| pr.(x, 3) } # -> [3, 6, 9]
Proc#curry を使うとブロックを使わなくて良くなる
pr = -> x, y { x * y }
[1,2,3].map(&pr.curry.(3)) # -> [3, 6, 9]
似たような関数を複数用意すること無く、部分適用を使って動的に生成できる
たとえば、FuzzBuzz
Proc#=== は、Proc#call と同じなので、case文のwhen式は、caseに渡した式を引数とするProcを渡す事ができる
fuzzbuzz = -> n {
case n
when -> n { (n % 15).zero? } then 'fuzzbuzz'
when -> n { (n % 3).zero? } then 'fuzz'
when -> n { (n % 5).zero? } then 'buzz'
else
n
end
}
p (1..100).to_a.map(&fuzzbuzz) # -> [1, 2, "fuzz", 4, "buzz", "fuzz" ...
when文に渡しているProcは部分適用を使えば、こんな風に書ける
fuzzbuzz = -> n {
curried = Proc.new {|base, n| (n % base).zero? }.curry
case n
when curried.(15) then 'fuzzbuzz'
when curried.(3) then 'fuzz'
when curried.(5) then 'buzz'
else
n
end
}
p (1..100).to_a.map(&fuzzbuzz) # -> [1, 2, "fuzz", 4, "buzz", "fuzz" ...