Scala クロージャ
Scala において、クロージャ(Closure)は関数の一種であり、その定義環境内の自由変数をキャプチャして含むことができます。
クロージャは関数であり、戻り値は関数の外部で宣言された1つ以上の変数に依存します。
クロージャは関数外部の変数にアクセスし、関数内でこれらの変数を使用できます。変数のスコープがその定義環境を超えても、クロージャは引き続きこれらの変数にアクセスできます。
クロージャの鍵となるのは、関数外部の変数を「閉じ込め」、これらの変数が関数内で引き続き使用できるようにすることです。
次に示す匿名関数のようなものです:
val multiplier = (i:Int) => i * 10
関数本体には変数 i があり、これは関数のパラメータとして機能します。次に示す別のコードのようになります:
val multiplier = (i:Int) => i * factor
multiplier には2つの変数 i と factor があります。そのうちの i は関数の仮パラメータであり、multiplier 関数が呼び出されるときに i には新しい値が代入されます。しかし、factor は仮パラメータではなく自由変数です。次のコードを考えてみてください:
var factor = 3 val multiplier = (i:Int) => i * factor
ここでは自由変数 factor を導入します。この変数は関数の外で定義されています。
このように定義された関数変数 multiplier は「クロージャ」になります。関数の外で定義された変数を参照しているためです。この関数を定義するプロセスは、この自由変数をキャプチャして閉じた関数を構成することです。
完全な例
例
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
上記のコードを実行すると、出力結果は次のとおりです:
$ scalac Test.scala $ scala Test muliplier(1) value = 3 muliplier(2) value = 6
次の例では、closure 関数が外部変数 externalVar をキャプチャしています。externalVar の値が変わると、クロージャ内の計算結果もそれに応じて変わります。
例
def main(args: Array[String]): Unit = {
var externalVar = 10
val closure = (x: Int) => x + externalVar
println(closure(5)) // 出力 15
externalVar = 20
println(closure(5)) // 出力 25
}
}
上記のコードを実行すると、出力結果は次のとおりです:
15 25
クロージャを戻り値として
次の例では、makeAdder 関数がクロージャを返します。異なるクロージャインスタンスは異なる adder 値をキャプチャします。
例
def main(args: Array[String]): Unit = {
def makeAdder(adder: Int): Int => Int = {
(x: Int) => x + adder
}
val addFive = makeAdder(5)
val addTen = makeAdder(10)
println(addFive(3)) // 出力 8
println(addTen(3)) // 出力 13
}
}
上記のコードを実行すると、出力結果は次のとおりです:
8 13
クロージャがキャプチャする変数の型
クロージャは、値型や参照型を含む異なる型の変数をキャプチャできます。
次の例では、addMessage クロージャが messages 変数をキャプチャし、それを変更できます。
例
def main(args: Array[String]): Unit = {
var messages = List("Hello", "World")
val addMessage = (msg: String) => messages = messages :+ msg
addMessage("Scala")
println(messages) // 出力 List(Hello, World, Scala)
}
}
上記のコードを実行すると、出力結果は次のとおりです:
List(Hello, World, Scala)
クロージャと関数型プログラミング
クロージャは関数型プログラミングにおける重要な概念であり、高階関数やカリー化などの技術で広く使用されています。
次の例では、highOrderFunction 関数が関数をパラメータとして受け取ります。渡されたクロージャは外部変数 externalVar をキャプチャします。
例
def main(args: Array[String]): Unit = {
def highOrderFunction(f: Int => Int, x: Int): Int = f(x)
val externalVar = 5
val closure = (x: Int) => x + externalVar
println(highOrderFunction(closure, 10)) // 出力 15
}
}
上記のコードを実行すると、出力結果は次のとおりです:
15その他の拡張