コード例
立方体の体積を計算するメソッドの例。
3辺の長さを(Int)で3つ受取り、乗算して(Int)で返却する cubicVolume
があるとします。
特にカリー化を考慮しない場合
引数を3つ定義し、呼び出しも数値を渡してやります。
scala> def cubicVolume(x: Int, y: Int, z: Int): Int = x * y * z
cubicVolume: (x: Int, y: Int, z: Int)Int
scala> cubicVolume(2, 3, 4)
res0: Int = 24
カリー化を行う場合
1.
まず、引数は()
でバラバラに定義します。
scala> def cubicVolume(x: Int)(y: Int)(z: Int): Int = x * y * z
cubicVolume: (x: Int)(y: Int)(z: Int)Int
2.
次に、cubicVolume
に 第一引数の(2)
を与え、更に後続の引数のためにプレースホルダー_
を付けてcubicVolume_X
に代入します。 そうすると、val cubicVolume_X
は、「Intを受け取って、「(Int => Int)という関数」を返す関数となります。
scala> val cubicVolume_X = cubicVolume(2)_
cubicVolume_X: Int => (Int => Int) = $$Lambda$...
3.
次に、 cubicVolume_X
に第二引数の(3)
を与えてcubicVolume_XY
に代入します。
そうすると、val cubicVolume_XY
は、「Intを受け取ってIntを返す関数」となります。
scala> val cubicVolume_XY = cubicVolume_X(3)
cubicVolume_XY: Int => Int = $$Lambda$...
4.
最後に、cubicVolume_XY
に第三引数の(4)
を与えてcubicVolumeResult
に代入します。
cubicVolume_XY(4)
はなにやら処理をして、数値を返します。 何やら処理、というのは、 x * y * z
の計算結果です。
ようやく24という数値が返ってきました。
scala> val cubicVolumeResult = cubicVolume_XY(4)
cubicVolumeResult: Int = 24
何が起こっているのか
カリー化を行う場合、イメージ的には以下のような処理になっているようです。
scala> def cubicVolume(x: Int): (Int) => ((Int) => Int) = {
|
| def cubicVolume_X(y: Int): (Int) => Int = {
|
| def cubicVolume_XY(z: Int): Int = {
| // Intを受け取って、Intを返す
| return x * y * z
| }
|
| // Intを受け取って、Intを返す関数、つまりcubicVolume_XYを返す
| return cubicVolume_XY _
|
| }
|
| // Intを受け取って、(Int => Int)を返す関数、つまりcubicVolume_Xを返す
| return cubicVolume_X _
| }
cubicVolume: (x: Int)Int => (Int => Int)
scala> val x = cubicVolume(2)
x: Int => (Int => Int) = $$Lambda$...
scala> val xy = x(3)
xy: Int => Int = $$Lambda$...
scala> val xyz = xy(4)
xyz: Int = 24
curried メソッドを使う例
Scalaにはcurriedというメソッドがあり、これは複数の引数をまとめてカリー化した関数を返してくれます。
// 先程と同じ、ごく普通のメソッド
scala> def cubicVolume(x: Int, y: Int, z: Int): Int = x * y * z
cubicVolume: (x: Int, y: Int, z: Int)Int
// メソッドを関数に
scala> val c = cubicVolume _
c: (Int, Int, Int) => Int = $$Lambda$...
// カリー化した関数を返す
scala> val cubicVolumeCurried = c.curried
cubicVolumeCurried: Int => (Int => (Int => Int)) = scala.Function3$$Lambda$...
scala> val cubicVolume_X = cubicVolumeCurried(2)
cubicVolume_X: Int => (Int => Int) = scala.Function3$$Lambda$...
scala> val cubicVolume_XY = cubicVolume_X(3)
cubicVolume_XY: Int => Int = scala.Function3$$Lambda$...
scala> val cubicVolumeResult = cubicVolume_XY(4)
cubicVolumeResult: Int = 24
参考にさせて頂きました
http://daybreaksnow.hatenablog.jp/entry/2013/10/02/185902
http://aroundthedistance.hatenadiary.jp/entry/2015/06/12/191051
※ イメージを掴むためと、理解が追いついていないため表現が厳密でない箇所があるかもしれません。