サーチ…
閉鎖の基礎
閉鎖とは、環境と一緒にとられる機能です。関数は通常、別の関数の中で定義された無名関数です。環境は、囲む関数の字句的範囲です(関数の字句的範囲の非常に基本的な考え方は、関数の中括弧の間に存在する範囲になります)。
func g() {
i := 0
f := func() { // anonymous function
fmt.Println("f called")
}
}
別の関数(例えばg )内で定義されている無名関数( fと言う)の中で、 fとgの両方のスコープに存在する変数にアクセスすることができます。しかし、クロージャの環境部分を構成するのはgのスコープであり(関数部分はf )、 gのスコープ内の変数に加えられた変更はその値を保持します(つまり、 f呼び出しの間に環境が存続します) 。
以下の関数を考えてみましょう:
func NaturalNumbers() func() int {
i := 0
f:= func() int { // f is the function part of closure
i++
return i
}
return f
}
上の定義では、 NaturalNumbersはNaturalNumbersが返す内部関数fがNaturalNumbersます。 f 、 NaturalNumbersのスコープ内で定義された変数iにアクセスしています。
私たちはNaturalNumbersからNaturalNumbersような新しい関数を取得します:
n := NaturalNumbers()
今、 nはクロージャーです。関連する環境( NaturalNumbersスコープ)も持つ関数( f定義)です。
n場合、環境部分には1つの変数のみが含まれますi
nは関数なので、呼び出すことができます:
fmt.Println(n()) // 1
fmt.Println(n()) // 2
fmt.Println(n()) // 3
上の出力から明らかなように、 nが呼び出されるたびに、 iインクリメントします。 iは0から開始し、 nへの各呼び出しはi++実行しi++ 。
iの値はコール間で保持されます。つまり、閉鎖の一部である環境が存続します。
NaturalNumbers再度呼び出すと、新しい関数が作成されて返されます。これは、 NaturalNumbers内の新しいiを初期化します。これは、新たに返された関数が、関数(まだf )ではなく新しい環境(新たに初期化されたi )で同じ部分を持つ別のクロージャを形成することを意味します。
o := NaturalNumbers()
fmt.Println(n()) // 4
fmt.Println(o()) // 1
fmt.Println(o()) // 2
fmt.Println(n()) // 5
nとo両方は、同じ機能部分(同じ動作を与える)を含むクロージャですが、異なる環境です。したがって、クロージャを使用すると、関数間で情報を保持するために使用できる永続的な環境に関数がアクセスできるようになります。
もう一つの例:
func multiples(i int) func() int {
var x int = 0
return func() int {
x++
// paramenter to multiples (here it is i) also forms
// a part of the environment, and is retained
return x * i
}
}
two := multiples(2)
fmt.Println(two(), two(), two()) // 2 4 6
fortyTwo := multiples(42)
fmt.Println(fortyTwo(), fortyTwo(), fortyTwo()) // 42 84 126