*Luaリファレンス 要注意点 ~クロージャ~ [#s3686e51]

-クロージャとは~
クロージャを知らない人にとってクロージャは理解しずらいものである。
しかし、実は、クラスの類似品と考えてよい。~
~
例えば、
#sh(csharp){{
-- C#的な何かメタな言語で書かれたカウンタークラス
class Counter {
    int iCurCnt;
    int iCurCnt; // 現在のカウント値

   // 生成時に初期値をコピー
    Counter(int iDefaultCnt) {
        iCurCunt = iDefaultCnt;
        iCurCnt= iDefaultCnt;
    }

    // 指定された値をカウントに増加。指定値がなければ、1増加
    IncCounter(int iIncValue = 1) {
        iCurCnt += iIncValue;
    }
}
}}
「ネストされた関数」を、「スコープ外から参照するいずこかに存在する変数のライフサイクル」が生存する限りにおいては、
   「該当のネストされた関数が参照するローカル変数」もまた、「元来のスコープを超えて生存する」、そのような機構、と考えておけば破綻しない。)
といったクラスがあるとします。~
「メソッドが生成時用メソッド(コンストラクタ)」と、他1つ(増加用メソッド)しかありません。~
このような''「生成時用」と「他1つ」''といった限定シチュエーションの場合、
クロージャを使えば簡易版クラスが作成出来る、といった程度のことです。
#sh(lua){{
-- luaのクロージャで書いた版。
function CreateCounter(iDefaultCnt)
    local iCurCnt = iDefaultCnt -- 生成時に値をとっておく。関数内のみに定義してあるので隠ぺいも出来ている。

**関数の定義は2種類 [#mb15204a]
-functionを前に置く書き方(シンタックスシュガー)
    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)も
元来のスコープを超えて生存し続けるわけです。

**クロージャの基本 [#k50af0e2]
-簡易クラスとして
#sh(lua){{
function foo (x)
    return 2*x
function newCounter ()
    local i = 0
    return function ()
               i = i + 1
               return i
           end
end

c1 = newCounter()
print(c1())  ---> 1
print(c1())  ---> 2
}}

-無名関数を変数に代入する書き方
-クロージャと隠蔽
#sh(lua){{
foo = function (x) return 2*x end
-- 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
}}


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS