3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go言語】 クロージャー

Last updated at Posted at 2024-09-18

前回までのあらすじ

第1回目は「型ごとの特徴」について学びました。もしよかったらご覧ください。

今回も引き続きUdemyの『【Go入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』の内容をもとに学習を進めていきます。

クロージャーとは

wikiでクロージャーを調べると以下のような説明がありました。

関数閉包はプログラミング言語における関数オブジェクトの一種。いくつかの言語ではラムダ式や無名関数にて利用>可能な機能・概念である。引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において>解決することを特徴とする。関数とそれを評価する環境のペアであるともいえる。

ちょっと何を言っているのかわからないですが、簡単にいうと関数の外で定義された変数の状態を保持し、後で使用することができる関数のことだと思っています。実際にコードベースで見た方がわかるかなと思うので早速実践していきます。

Go言語でクロージャーを書いてみた。

func AddCounter() func() int {
	var store int = 1
	return func() int {
		s := store
		store++
		return s
	}
}

func main() {
	f := AddCounter()
	fmt.Println(f()) // 1
	fmt.Println(f()) // 2
	fmt.Println(f()) // 3
	fmt.Println(f()) // 4
}

これがクロージャーというものです。
main()f := AddCounterfに代入してから、4回呼び出すとそれぞれ違う値が返ってきます。
これはAddCounter()が変数の状態を維持しており、関数が呼ばれるたびに前回の状態を保持した状態から処理を始めるからです。仕組みを1つずつみていきましょう。

f := AddCounter()との記載があるため、fには以下の無名関数が入ってきます。

func() int {
		s := store
		store++
		return s
	}

1回目に呼び出した時の処理からみていきましょう。呼び出された時にAddCounter()store1です。結果的に最初の処理では1を出力します。

//AddCounter()で定義されている変数
var store int = 1

func() int {
		s := store // sにstore(1)を入れる。
		store++    // store = 2 になる。
		return s   // 現在sには1が入っている。
	}

2回目です。呼び出された時にAddCounter()store2になります。なぜかというと、先ほどのstore++の処理でstore = 2になっているからです。

//AddCounter()で定義されている変数
var store int = 2

func() int {
		s := store // sにstore(2)を入れる。
		store++    // store = 3 になる。
		return s   // 現在sには2が入っている。
	}

あとはこれの繰り返しです。

//3回目
//AddCounter()で定義されている変数
var store int = 3

func() int {
		s := store // sにstore(3)を入れる。
		store++    // store = 4 になる。
		return s   // 現在sには3が入っている。
	}

//4回目
//AddCounter()で定義されている変数
var store int = 4

func() int {
		s := store // sにstore(4)を入れる。
		store++    // store = 5 になる。
		return s   // 現在sには4が入っている。
	}

結果的に関数を呼ぶたびに数値が増えるということになります。

使い道

状態を持つ関数
関数が内部状態を持つようにすることができます。例えば、カウンターを保持する関数を作成できます。

ファクトリ関数
特定の設定に基づいて異なる動作をする関数を作成できます。動的に異なる振る舞いを持つ関数を生成することができます。

データのカプセル化
外部変数の状態を閉じ込め、外部から直接アクセスできないようにしながら、制御された方法でアクセス・変更できる関数を作るのに役立ちます。

という感じの使い方があるそうです。ただ、私はまだ使っているところに出会っていないです。使い方によっては面白そうなことはできそうな感じがしますね。

ジェネレーターとは

必要な時に一つずつ値を順次生成するものジェネレーターと呼び、上記のクロージャーの例もその1つになります。今回は1ずつ増やすという簡単な実装でしたが、フィボナッチ数列を生成するジェネレーターなども応用すれば作ることができます。ぜひ興味がある人は作ってみてください。

ジェネレーターの注意点

当たり前と言えば当たり前ですが、変数名を変えてジェネレーターを実行するとそれぞれで値を保持するようになります。

func AddCounter() func() int {
	var store int = 1
	return func() int {
		s := store
		store++
		return s
	}
}

func main() {
f := AddCounter()
fmt.Println(f()) // 1
fmt.Println(f()) // 2
fmt.Println(f()) // 3
fmt.Println(f()) // 4

f2 := AddCounter()
fmt.Println(f2()) // 1
fmt.Println(f2()) // 2
fmt.Println(f2()) // 3
fmt.Println(f2()) // 4    
}

まとめ

是非お目にかかりたいです。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?