Go言語始めてみようと思い、先日下記の参考書をポチりました。
まず手始めに基本的なシンタックスとGo言語特有のイディオムを洗っていこうと思い打鍵確認したところ、いきなり取っつきにくい考え方に直面したのです。。
折角なので自身の理解を深めるために頑張って記事にまとめてみようと思います!!
クロージャとしての無名関数
(参考書引用)
Goの無名関数は「クロージャ」です。クロージャは日本語では「関数閉包」と呼ばれ、関数と関数の処理に関係する「関数外」の環境をセットにして「閉じ込めた(閉包)」ものです。
なるほど、わからん。サンプルがあったのでみてみることにします。
package main
import "fmt"
// laterは「引数に文字列を取り、戻り値に文字列を返す関数」を返す関数
func later() func(string) string {	
	// storeはクロージャ内で使われている変数と結び付いた変数
	// クロージャと結びついている限り破棄されない
	var store string
	fmt.Println("store: " + store) // debug
	
	return func(next string) string { // F1(クロージャ)
		fmt.Println("next: " + next) //debug
		s := store // storeから一つ前に呼び出された文字列を取り出す
		store = next // 今受け取っている文字列をstoreに格納
		return s // 一つ前の文字列を返す
	}
}
func main() {
	f := later()
	fmt.Println("a: " + f("Golang"))
	fmt.Println("b: " + f("is"))
	fmt.Println("c: " + f("awesome!"))
}
/* 出力
> store: 
> next: Golang
> a: 
> next: is
> b: Golang
> next: awesome!
> c:is
*/
引数として渡した文字列が、次の関数呼び出しのタイミングで返ってきていることがわかりますね。
なぜこのような動作になるのかを丁寧に追ってみたいと思います。
f := later()の挙動
まずf := later()でfが受けるのはlater()の戻り値である。つまり「引数に文字列を取り、戻り値に文字列を返す関数」(以降F1と呼ぶこととする)を受け取る。ここでF1の関数内では変数storeが使用されているが、これはlater()関数のローカル変数として定義されたものである。通常、ローカル変数は関数の実行が終了した後は破棄されるはずである。しかし、本ケースのようにクロージャ内部で変数が使用される場合、内部的にはクロージャに属する変数として機能する。具体的には、クロージャが参照されている限り変数storeは破棄されない。(f := later()のfを使用している限りstoreの状態は保持される)
f("Golang")の挙動
- はじめにF1の引数nextに"Golang"が渡される。
- 
s := storeはstoreが空なのでsには""が返る。
- 
store = nextでstoreに"Golang"が渡られる。
- 戻り値は空。
f("is")の挙動
- 
s := storeでstoreに格納されているのはf("Golang")時に渡された"Golang"であるから、sには"Golang"が格納される。ここがポイント。
- 
store = nextでstoreに"is"が渡される。
- 戻り値は"Golang"
このように、クロージャの機能によって、確かにstore内の状態が保持されていることがわかります。
この機能の何が嬉しいのかはまた今度調べてまとめてみよう、、今回はここまで!
