クロージャとは、関数と関数内で参照する変数をまとめた機能のことです。
引数で指定されない変数を、内部に保持する事ができます。
Goでは、返り値に無名関数を使用して実現します。
func incrementGenerator() func() int {
x := 0 // 内部で使用する変数を、代入時に1度だけ初期化
return func() int {
x++
return x
}
}
func main() {
counter := incrementGenerator() // x := 0と無名関数の代入が行われる
fmt.Println(counter()) // 1 無名関数の内部の処理だけが行われる
fmt.Println(counter()) // 2 同上
}
また、クロージャの返り値を複数にして、以下のようにリセット用関数を返すこともできます。
func incrementGenerator() (func() int, func()) {
x := 0 // 内部で使用する変数を代入時に1度だけ初期化
gen := func() int {
x++
return x
}
reset := func() {
x = 0
}
return gen, reset
}
func main() {
counter1, reset1 := incrementGenerator() // x := 0と無名関数の代入が行われる
counter2, _ := incrementGenerator() // x := 0と無名関数の代入が行われる。resetは使用しないため破棄
fmt.Println(counter1()) // 1
fmt.Println(counter1()) // 2
fmt.Println(counter2()) // 1
reset1()
fmt.Println(counter2()) // 2 counter2のxはreset1()の影響を受けない
fmt.Println(counter1()) // 1
}
クロージャの変数は、他クロージャの変更による影響を受けません。
クロージャは、異なる条件を保存して計算するときに便利です。
例えば消費税率を保存して計算するために、次のようできます。
(切り捨てなどは考慮していません)
func bill(tax float64) func(total float64) float64 {
return func(total float64) float64 {
return total * (1 + tax)
}
}
func main() {
price8 := bill(0.08)
fmt.Println(price8(100)) // 108
fmt.Println(price8(200)) // 216
price5 := bill(0.05)
fmt.Println(price5(100)) // 105
fmt.Println(price5(200)) // 210
}
次のようにクロージャの引数に関数を用いると、
さらに複雑なパターン分けに対応する事ができます。
func sliceCalc(f func(x int) int) func([]int) []int {
return func(ary []int) []int { // 短縮のため、ここで変数名aryを記述
buff := make([]int, len(ary)) // 結果を格納するスライスの作成
for i, v := range ary { // for rangeは配列やスライスのloopを簡単にできる。indexとvalueを返す
buff[i] = f(v) // スライスの各要素を引数の関数で計算し、結果格納用スライスに格納
}
return buff
}
}
func square(x int) int{
return x * x
}
func x2(x int) int {
return x * 2
}
func main() {
a := []int {1, 2, 3, 4, 5}
squareSlice := sliceCalc(square)
x2Slice := sliceCalc(x2)
fmt.Println(squareSlice(a)) // [1 4 9 16 25]
fmt.Println(x2Slice(a)) // [2 4 6 8 10]
fmt.Println(sliceCalc(square)(a)) // この様にも書ける
fmt.Println(sliceCalc(x2)(a)) // この様にも書ける
}
好ましいかどうかは状況次第ですが、引数とする関数内で分岐すれば、パターン分けは広がります。
参考
www.geocities.jp