Scala
サンプル
カリー化
備忘録
currying

Scalaにおけるカリー化(Currying)サンプル

More than 1 year has passed since last update.


コード例

立方体の体積を計算するメソッドの例。

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.

まず、引数は()でバラバラに定義します。


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)という関数」を返す関数となります。


2

scala> val cubicVolume_X = cubicVolume(2)_

cubicVolume_X: Int => (Int => Int) = $$Lambda$...

3.

次に、 cubicVolume_Xに第二引数の(3)を与えてcubicVolume_XYに代入します。

そうすると、val cubicVolume_XYは、「Intを受け取ってIntを返す関数」となります。


3

scala> val cubicVolume_XY = cubicVolume_X(3)

cubicVolume_XY: Int => Int = $$Lambda$...

4.

最後に、cubicVolume_XYに第三引数の(4)を与えてcubicVolumeResultに代入します。

cubicVolume_XY(4)はなにやら処理をして、数値を返します。 何やら処理、というのは、 x * y * z の計算結果です。

ようやく24という数値が返ってきました。


4

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

※ イメージを掴むためと、理解が追いついていないため表現が厳密でない箇所があるかもしれません。