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
内の状態が保持されていることがわかります。
この機能の何が嬉しいのかはまた今度調べてまとめてみよう、、今回はここまで!