Closure(クロージャ) とは、関数が実行された時にその静的スコープで定義された変数を利用できる関数です。
使ったコード - framinal-new-tools/go-closure at main · lirlia/framinal-new-tools · GitHub
こんなコードを書いてみると、counter1
を呼び出すたびにインクリメントされます。これは getCounter()
クロージャの変数の静的スコープにある count
変数を保持し続けているのでうまいこと利用できています。
package main import "fmt" func main() { counter1 := getCounter() fmt.Println(counter1()) // 1 fmt.Println(counter1()) // 2 } func getCounter() func () int{ count := 0 return func() int { count++ return count } }
同じ関数を別に呼ぶと
同じ関数を呼び出し counter1
/ counter2
で分けてみます。
また getCounter
関数の呼び出し時に count 値によって返す関数を変えてみます。
package main import "fmt" func main() { counter1 := getCounter() counter2 := getCounter() fmt.Println(counter1()) // 1 fmt.Println(counter1()) // 2 fmt.Println(counter2()) // 1 fmt.Println(counter2()) // 2 fmt.Println(counter2()) // 3 } func getCounter() func () int{ count := 0 if count == 0 { return func() int { count++ return count } } return func() int { count += 2 return count } }
これを実行すると以下の値が返ってきます。
1 2 1 2 3
このことから、counter1
や counter2
変数にはこの関数と、count の山椒をもちづつけていることがわかります。
return func() int { count++ return count }
同じ関数で複数のクロージャを作る
getCounter2()
で二つのクロージャを返して実行してみます。
package main import "fmt" func main() { counter3, counter4 := getCounter2() fmt.Println(counter3()) // 1 fmt.Println(counter3()) // 2 fmt.Println(counter4()) // 3 fmt.Println(counter4()) // 4 } func getCounter2() (f1 func () int, f2 func () int){ count := 0 f1 = func() int { count++ return count } f2 = func() int { count++ return count } return f1, f2 }
これを実行すると以下の値が返ってきます。
1 2 3 4
これは f1()
と f2()
が count 変数の参照を共有しているためですね。(このケースでは mutex をとって排他制御をする必要がある)
ポインタをクロージャに渡す
func main() { i := 0 counter5:= getCounter3(&i) fmt.Println(counter5()) // 1 i++ fmt.Println(counter5()) // 3 } func getCounter3(count *int) func () int{ return func() int { *count++ return *count } }
このケースでは以下の値が返ってきます。
1 3
ポインタを渡しているので、別のところでインクリメントすればそれがそのまま反映されますね。このケースでもポインタをそのまま渡すのではなく mutex を含めて渡す必要がありますね。
type value struct { i int mu sync.Mutex }