*Luaリファレンス 要注意点 ~クロージャ~ [#s3686e51] -クロージャとは~ クロージャを知らない人にとってクロージャは理解しずらいものである。 しかし、実は、クラスの類似品と考えてよい。~ ~ 例えば、 #sh(csharp){{ -- C#的な何かメタな言語で書かれたカウンタークラス class Counter { int iCurCnt; // 現在のカウント値 // 生成時に初期値をコピー Counter(int iDefaultCnt) { iCurCnt= iDefaultCnt; } // 指定された値をカウントに増加。指定値がなければ、1増加 IncCounter(int iIncValue = 1) { iCurCnt += iIncValue; } } }} といったクラスがあるとします。~ 「メソッドが生成時用メソッド(コンストラクタ)」と、他1つ(増加用メソッド)しかありません。~ このような''「生成時用」と「他1つ」''といった限定シチュエーションの場合、 クロージャを使えば簡易版クラスが作成出来る、といった程度のことです。 #sh(lua){{ -- luaのクロージャで書いた版。 function CreateCounter(iDefaultCnt) local iCurCnt = iDefaultCnt -- 生成時に値をとっておく。関数内のみに定義してあるので隠ぺいも出来ている。 return function(iIncValue) -- 関数そのものを返す。 iIncValue = iIncValue or 1 iCurCnt = iCurCnt + iIncValue return iCurCnt end end local IncCounter = CreateCounter(5) print(IncCounter()) print(IncCounter(5)) print(IncCounter(2)) }} ~ クロージャには、クラスとは別の「そのローカル変数の隠蔽性」に着目した応用方法もありますが、~ 最初はこのように理解しておきましょう。~ ~ クロージャ自体は:~ >~ 「ネストされた関数(=A)」を、「スコープ外から参照するいずこかに存在する変数のライフサイクル」が生存する限りにおいては、~ 「該当のネストされた関数(=A)が参照するローカル変数」もまた、「元来のスコープを超えて生存する」、そのような機構 >~ 即ち、上の例でいうと、~ CreateCounter() で一度 function オブジェクトが返ります。~ そうすると、''IncCounter で参照しているので、local iCurCnt は関数の実行が終わっても~ 消滅しないようになる''のです。~ ~ クロージャにより、''IncCounter が、この関数オブジェクトを参照している間は、local iCurCnt も消滅しないようになる''のです。~ ~ 即ち、Aのライフサイクルが生存しているので、Aが参照しているローカル変数(iCurCnt)も 元来のスコープを超えて生存し続けるわけです。 **クロージャの基本 [#k50af0e2] -簡易クラスとして #sh(lua){{ function newCounter () local i = 0 return function () i = i + 1 return i end end c1 = newCounter() print(c1()) ---> 1 print(c1()) ---> 2 }} -クロージャと隠蔽 #sh(lua){{ -- math.sin の関数を置き換えたい。しかしそれに付随する変数は全て隠ぺいしたい場合 do local oldSin = math.sin local k = math.pi/180 math.sin = function(x) return oldSin(x*k) end end }} **イタレータとクロージャ [#fbc2b632] -python 他と同じ。for in が対応していることも含めて標準的) #sh(lua){{ function values (t) local i=0 return function () i=i+1 return t[i] end end t = {10,20,30} for element in values(t) do print(element) end for element in values(t) do print(element) end }} このように2回繰り返しても、ちゃんとfor in 単位でクロージャがリセットされるので期待通りの挙動となる。~ ~ 以下のようにvtを用意してしまうと、当然変数寿命が維持されてしまい、上手く動かなくなるので注意 #sh(lua){{ t = {10,20,30} vt = values(t) for element in vt do print(element) end for element in vt do print(element) end }}