Go言語(golang)のクロージャ(closure)
クロージャの使い方を説明します。Tour of Goを参考にしています。
Go言語(golang)のクロージャ(closure)の使い方
Go言語のクロージャ(closure)は、関数とその関数が定義された環境(変数などのコンテキスト)を包括的に保持するものです。クロージャは、関数内で定義された関数で、外部の変数を参照できます。以下に、クロージャの基本的な例を示します。
package main
import "fmt"
func main() {
// クロージャを定義して変数に代入
add := func(x, y int) int {
return x + y
}
// クロージャの使用
result := add(3, 4)
fmt.Println("Result:", result)
}
この例では、add
という名前の変数にクロージャを代入しています。このクロージャは、引数 x
と y
を受け取り、それらの値を加算して返す関数です。
クロージャは、外部の変数にアクセスすることができます。例えば、以下の例では、クロージャが外部の変数 counter
にアクセスして、その値を保持しています。
package main
import "fmt"
func main() {
// クロージャで外部変数にアクセス
counter := 0
increment := func() int {
counter++
return counter
}
// クロージャの使用
fmt.Println(increment()) // 1
fmt.Println(increment()) // 2
}
この例では、increment
クロージャは counter
変数にアクセスして、その値を1ずつ増やしています。クロージャは外部変数をキャプチャすることで、その変数の状態を保持し続けることができます。
クロージャは無名関数としても使われることがあり、関数を変数に代入して扱ったり、関数の引数として渡したりする場面で役立ちます。
Go言語(golang)のクロージャ(closure)の注意点
変数のキャプチャ
クロージャは外部の変数をキャプチャします。そのため、クロージャ内で変数が変更されると、同じ変数を参照する他のクロージャやコードにも影響が及びます。
ループ変数の注意
クロージャ内でループ変数を使用すると、期待通りに動作しない可能性があります。ループ変数はイテレーションごとに新しい変数が作成されるため、クロージャ内でそれを参照すると最終的な値が予測できなくなります。この問題を解決するためには、ループ変数をループ内で明示的に引数として渡す必要があります。
package main
import "fmt"
func main() {
var funcs []func()
for i := 0; i < 3; i++ {
// クロージャがループ変数をキャプチャする例(誤った使い方)
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f() // すべてのクロージャが最終的な i の値を表示
}
}
結果
3
3
3
上記の例では、すべてのクロージャが同じ i
の値を参照してしまいます。正しい方法はループ変数を引数として渡すことです。
package main
import "fmt"
func main() {
var funcs []func()
for i := 0; i < 3; i++ {
// クロージャが正しくループ変数をキャプチャする例
funcWithArg := func(x int) func() {
return func() {
fmt.Println(x)
}
}(i)
funcs = append(funcs, funcWithArg)
}
for _, f := range funcs {
f() // それぞれのクロージャが期待通りの値を表示
}
}
結果
0
1
2