Luaリファレンス 要注意点 ~クロージャ~†
クロージャとは
クロージャを知らない人にとってクロージャは理解しずらいものである。
しかし、実は、クラスの類似品と考えてよい。
例えば、
-- C#的な何かメタな言語で書かれたカウンタークラス
class Counter {
int iCurCnt; // 現在のカウント値
// 生成時に初期値をコピー
Counter(int iDefaultCnt) {
iCurCnt= iDefaultCnt;
}
// 指定された値をカウントに増加。指定値がなければ、1増加
IncCounter(int iIncValue = 1) {
iCurCnt += iIncValue;
}
}
といったクラスがあるとします。
「メソッドが生成時用メソッド(コンストラクタ)」と、他1つ(増加用メソッド)しかありません。
このような「生成時用」と「他1つ」 といった限定シチュエーションの場合、
クロージャを使えば簡易版クラスが作成出来る、といった程度のことです。
-- 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 も消滅しないようになる のです。
即ち、Aのライフサイクルが生存しているので、Aが参照しているローカル変数(iCurCnt)も
元来のスコープを超えて生存し続けるわけです。
クロージャの基本†
イタレータとクロージャ†
python 他と同じ。for in が対応していることも含めて標準的)
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を用意してしまうと、当然変数寿命が維持されてしまい、上手く動かなくなるので注意
t = {10,20,30}
vt = values(t)
for element in vt do
print(element)
end
for element in vt do
print(element)
end