Go言語のクロージャとは?関数内関数の活用例と仕組み
生徒
「Go言語っていうプログラミングで、クロージャって聞いたんですけど、何なんですか?」
先生
「いい質問ですね。クロージャとは、関数の中で作られた関数のことです。Go言語では関数を他の関数の中で定義して、記憶させることができるんですよ。」
生徒
「えっ?関数の中に関数があるんですか?それって何か便利なんですか?」
先生
「とっても便利ですよ!じゃあ、仕組みと使い方を具体的に見ていきましょう!」
1. クロージャとは?基本の考え方を理解しよう
Go言語(Golang)のクロージャとは、関数の中で定義された関数が、外側の関数の変数(環境)を記憶して持ち運べる仕組みのことです。外側で用意した値や状態を、内側の関数が後から参照・更新できます。これは関数を値として扱えるGo言語の特性とセットで理解すると分かりやすく、短いコードで「状態を覚える処理」を書けるのが特徴です。
身近な例に置き換えると、台所の蛇口(内側の関数)は、どの水道管(外側の変数)につながっているかを覚えています。蛇口をひねるたびに、同じ水道管から水(前回の状態を引き継いだ値)が流れてくる、というイメージです。
次の超短い例では、外側で用意した回数カウンタを、内側の関数が覚えて増やし続けます。ここでは「関数が外側の変数をキャプチャして記憶する」点だけを体験できれば十分です。
package main
import "fmt"
func main() {
counter := func() func() int {
n := 0
return func() int {
n++
return n
}
}()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
}
このコードでは、内側の無名関数が外側の変数nを覚えています。counter()を呼ぶたびにnが増え、前回の値が次回へ引き継がれます。これがGo言語のクロージャの基本です。キーワードは「関数の内部関数」「外側の変数をキャプチャ」「状態を保つ」「あとから呼んでも覚えている」です。
2. クロージャを使った簡単な例
ここでは、Go言語のクロージャ(関数内関数)が「外側の変数を覚えて使い続ける」様子を、最小のコードで体験します。ポイントは、戻り値として返した無名関数が、外側スコープの変数をキャプチャして、次の呼び出しでも同じ状態を参照できることです。
package main
import "fmt"
func main() {
adder := createAdder() // 関数を作って受け取る(状態を持つ関数)
fmt.Println(adder(5)) // 5
fmt.Println(adder(3)) // 8(前回の合計に3を加算)
fmt.Println(adder(10)) // 18(合計を覚えている)
}
// 合計を覚えるクロージャを返すファクトリ関数
func createAdder() func(int) int {
sum := 0 // 外側の変数(状態)
return func(x int) int { // 無名関数がsumをキャプチャ
sum += x
return sum
}
}
上のコードでは、createAdderが「合計を覚える関数」を返し、返された関数はsumという変数を記憶したまま動きます。呼び出すたびにsumが更新されるため、状態を維持しながら計算を続けることができます。これは、カウンタやスコア加算、累積合計などの処理にそのまま応用できます。
初心者の方は、次の観点で読み解くと理解しやすくなります。
- 関数を変数に代入できる(
adderは「関数」という値) - 返された無名関数が外側の変数(
sum)を覚える(キャプチャ) - 呼び出すたびに同じ状態が使われるので結果が積み上がる
このように、Go言語のクロージャは「簡潔なコードで状態を扱う」ための強力な道具です。キーワードは無名関数・変数キャプチャ・状態保持・関数を返すです。
3. 実行結果を確認してみよう
上のプログラムを実行すると、以下のような出力になります。
5
8
18
呼び出すたびに、足した結果が蓄積されているのがわかります。これは、sumの値をクロージャが覚えているからです。
4. なぜクロージャが便利なの?
クロージャを使うと、「ある状態を覚えておく関数」を作ることができます。普通の関数は毎回ゼロから計算をしますが、クロージャは前の計算結果を記憶して次に活かすことができます。
これにより、同じ処理を繰り返すような場面や、特定の条件に応じて振る舞いが変わる関数を作るのに非常に役立ちます。
5. 関数を変数のように扱えるGo言語の特性
Go言語では、関数も値として変数に代入することができます。これがクロージャを活用するうえでとても重要なポイントです。
関数を変数に入れて、その変数を使って関数を呼び出すことで、まるで特別な処理機能を持った変数のように扱うことができるのです。
6. 複数のクロージャを作るとどうなる?
では、createAdderをもう一度呼び出したらどうなるでしょうか?実験してみましょう。
package main
import "fmt"
func main() {
adder1 := createAdder()
adder2 := createAdder()
fmt.Println(adder1(1)) // 出力: 1
fmt.Println(adder1(2)) // 出力: 3
fmt.Println(adder2(10)) // 出力: 10
fmt.Println(adder2(20)) // 出力: 30
}
func createAdder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
結果は以下のようになります:
1
3
10
30
adder1とadder2は別々のsumを持っていて、お互いの影響を受けません。これが状態を持つ関数の力です。
7. クロージャが使われる場面とは?
クロージャは、次のような場面でよく使われます:
- 何かの処理を段階的に進めたいとき(状態を記憶)
- 条件に応じて関数の動きを変えたいとき
- 関数を引数として渡すときに、その関数に特定の値を記憶させておきたいとき
たとえばゲームで「スコアを足していく処理」や「買い物の合計金額を覚えていく処理」などにも使えます。
まとめ
この記事ではGo言語におけるクロージャについて、その仕組みと活用方法をやさしく解説しました。クロージャは、外側の関数の変数を覚えて利用できる関数内関数のことで、Go言語の大きな特徴の一つです。通常の関数は呼び出すたびに新しい処理を行いますが、クロージャは「状態を保持し続けること」ができます。そのため、累積計算、状態管理、イベント処理、カスタマイズされた処理の作成など、実践的なプログラミングで非常に役立ちます。
さらに、クロージャはGo言語の「関数も値として扱える」という特性と組み合わさることで、柔軟かつ強力なコード設計を可能にします。特にWeb開発やAPI設計、ゲーム開発など多様な場面で利用され、コードの再利用性や拡張性を高めます。たとえば「カウンター機能」や「リスナー関数」などは典型的なクロージャの活用例です。
以下に、もう一度整理したサンプルコードを載せておきます。クロージャが変数を記憶して、呼び出しのたびに異なる結果を返す様子を確認できます。
package main
import "fmt"
func main() {
counter := createCounter()
fmt.Println(counter()) // 出力: 1
fmt.Println(counter()) // 出力: 2
fmt.Println(counter()) // 出力: 3
}
func createCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
この例ではcreateCounterが新しい関数を返し、その関数はcountという外側の変数を覚えています。呼び出すたびにcountが増えていくのは、まさにクロージャの特徴です。このようにクロージャを活用することで、Go言語プログラミングでは状態を扱いやすく、シンプルで明快なコードを実現できます。
生徒
「先生、今日の記事でクロージャのことがよく分かりました!Go言語では関数の中に関数を作れて、その関数が変数を覚えてくれるんですよね?」
先生
「その通りです。外側の変数を覚える関数、それがクロージャです。例えばカウンターを作るときや、計算の途中経過を保持したいときにとても便利ですね。」
生徒
「なるほど!普通の関数は呼ぶたびにリセットされるけど、クロージャなら前の状態を持ったまま次に進めるんですね。これならスコア計算や買い物の合計金額を覚えておく処理にも使えそうです!」
先生
「そうですね。Go言語では関数を値として扱えるので、クロージャと組み合わせるとプログラミングの幅がぐっと広がります。Webアプリやゲーム開発、サーバーサイドのAPI処理でもよく使われていますよ。」
生徒
「わあ、すごい!実際にサンプルプログラムを動かしてみたら、呼び出すたびに数字が増えていくのが本当に面白かったです。Go言語のクロージャをもっと練習して、自分のプログラムにも使ってみたいです!」
先生
「素晴らしい意欲ですね。クロージャをしっかり理解すれば、より高度なGo言語プログラミングにも自信を持って取り組めるようになりますよ。」