Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
クロージャ / 無名関数
Goでは関数を値として変数に代入したり、他の関数の引数や戻り値にしたりできます。クロージャは定義された時点の変数を「閉じ込める」関数で、関数型プログラミングの基本的なパターンです。
構文
// 無名関数(即時実行)
func() {
// 処理
}()
// 変数に代入します。
add := func(a, b int) int {
return a + b
}
result := add(3, 4) // 7
// クロージャ(外側のスコープの変数をキャプチャします)
counter := 0
increment := func() int {
counter++ // 外側の変数counterを参照します。
return counter
}
// 関数を返す高階関数(クロージャファクトリ)
func makeAdder(x int) func(int) int {
return func(y int) int {
return x + y // xはクロージャにキャプチャされます。
}
}
クロージャの活用パターン
| パターン | 概要 |
|---|---|
| 即時実行関数 | 定義と同時に実行します。初期化処理や名前空間の分離に使います。 |
| 状態のカプセル化 | クロージャが変数を閉じ込めることで、外部からアクセスできない状態を実現します。 |
| ファクトリ関数 | パラメータを設定した関数を生成して返します。 |
| コールバック | 関数を引数として渡す際に無名関数を使います。 |
サンプルコード
package main
import "fmt"
// カウンターを生成するファクトリ関数です。
func makeCounter(start int) func() int {
count := start
return func() int {
count++ // countはクロージャにキャプチャされています。
return count
}
}
// 乗算関数を生成するファクトリ関数です。
func multiplier(factor int) func(int) int {
return func(n int) int {
return n * factor
}
}
// 関数を引数として受け取る高階関数です。
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, n := range nums {
result[i] = fn(n)
}
return result
}
func main() {
// クロージャで状態をカプセル化します。
counter1 := makeCounter(0)
counter2 := makeCounter(10) // 別々の状態を持ちます。
fmt.Println(counter1()) // 1
fmt.Println(counter1()) // 2
fmt.Println(counter2()) // 11
fmt.Println(counter1()) // 3(counter2の影響を受けません)
fmt.Println()
// ファクトリ関数でカスタマイズした関数を生成します。
double := multiplier(2)
triple := multiplier(3)
nums := []int{1, 2, 3, 4, 5}
fmt.Println("2倍:", apply(nums, double))
fmt.Println("3倍:", apply(nums, triple))
fmt.Println()
// インラインで無名関数を渡します。
fmt.Println("2乗:", apply(nums, func(n int) int {
return n * n
}))
// 即時実行関数による初期化
result := func(a, b int) int {
return a + b
}(100, 200)
fmt.Println("即時実行の結果:", result)
}
概要
Goでは関数はファーストクラスの値として扱われます。クロージャはキャプチャした変数への参照を保持するため、クロージャを通じて変数の値を変更できます。これを利用してオブジェクト指向のカプセル化に似た設計を実現できます。
ループ内でgoroutineを起動するクロージャは、ループ変数を正しくキャプチャできないバグが有名です。ループ変数をクロージャの引数として渡すか、ループ内でローカルコピーを作成してください。Go 1.22以降ではループ変数の扱いが変更されています。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。