scala関数コリー化(Currying)

2178 ワード

定義#テイギ#
コリー化(Currying)とは,2つのパラメータを元に受け入れた関数を1つのパラメータを受け入れる関数に変える過程である.新しい関数は、元の2番目のパラメータをパラメータとする関数を返します.関数を定義します.
def add(x:Int,y:Int)=x+y

では、私たちが適用するときは、add(1,2)をこのように使うべきです.
この関数を変形します.
def add(x:Int)(y:Int) = x + y

では、私たちが適用するときは、add(1)(2)、最後の結果は同じ3で、この方法(プロセス)をコリー化と呼ぶべきです.
インプリメンテーションプロセス
add(1)(2)は実際には2つの一般関数(非コリー化関数)を順次呼び出し,1回目の呼び出しは1つのパラメータxを用い,1つの関数タイプの値を返し,2回目のパラメータyを用いてこの関数タイプの値を呼び出す.
実質的に最初にこのような方法に進化しました
def add(x:Int)=(y:Int)=>x+y

では、この関数はどういう意味ですか.1つのxをパラメータとして受信し、1つの匿名関数を返します.この匿名関数の定義は、1つのInt型パラメータyを受信し、関数体はx+yです.次に、このメソッドを呼び出します.
val result = add(1) 

resultを返します.resultの値は匿名の関数です.(y:Int)=>1+y
結果を得るためにresultを呼び出し続けます
val sum = result(2)

最後に印刷した結果は3です.
scalaソースコードの一例を見てみましょう.
Scalaコレクションtrait TransversableOnceでfoldLeftが定義されています.
 def foldLeft[B](z: B)(op: (B, A) => B): B = {
    var result = z
    this foreach (x => result = op(result, x))
    result
  }


この関数には2つのパラメータが必要で、まず汎用Bタイプのz、次に字面関数opop戻りタイプが汎用B.関数内ではまずzresultに割り当て、現在のオブジェクトのforeach関数を呼び出して各要素を巡り、op関数をresultに割り当て、最後にresultの値を返します.
初期値0からfoldLeftは、リスト内の各要素と、以前に蓄積された値に関数(m,n)=>m+nを順次適用します.
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val res = numbers.foldLeft(0)((m, n) => m + n)
print(res) // 55

最初の例のように、多重化の目的を達成するために、複数のパラメータリストの一部のパラメータリスト(上記のzのように)を与えて、新しい関数(partially applied function)を形成することができます.
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val numberFunc = numbers.foldLeft(List[Int]())_

val squares = numberFunc((xs, x) => xs:+ x*x)
print(squares.toString()) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

val cubes = numberFunc((xs, x) => xs:+ x*x*x)
print(cubes.toString())  // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)

参照先:https://docs.scala-lang.org/zh-cn/tour/multiple-parameter-lists.html
https://www.runoob.com/scala/currying-functions.html