閉鎖と価値の向上
Luaが新しい関数を作成すると、関数自体のプロトタイプと、関数で使用される外部変数または定数を含む関数オブジェクトが作成されます。新しい関数が関数内にある場合、このオブジェクトをローカル変数であるClosureと呼びます。それが参照する外部関数のアップバリューと呼ばれます。
以下に例を示します。
function f1(n)
local upvalue = "hello"
local f2 = function()
print(upvalue .. " " .. n)
end
return f2
end
g1 = f1(500)
g1()
print(g1)
g2 = f1(1500)
g2()
print(g2)
$ luajit copy.lua
$ hello 500
$ function: 0x00921ae0
$ hello 1500
$ function: 0x00926f28
f1を呼び出すたびに、関数プロトタイプとしてf2を使用した新しい関数(クロージャ)と2つのアップバリュー(n、upvalue)が生成されます。新しいクロージャはそれぞれ同じ関数プロトタイプを保持し、異なるアップ値を格納します。厳密に言えば、クロージャはプログラムの実行フェーズ中に決定される動的な概念。
アップバリュー値の範囲
アップバリューは、実際にはクロージャの外部関数のローカル変数であり、外部関数のスタックに存在します。外部関数の実行中に、クロージャは外部関数スタックのアップバリュー参照にアクセスすることによってアップバリューにアクセスします。外部関数の呼び出しプロセスが完了し、関数のスタックがクリアされ、アップバリューもクリアされます。この時点で、クロージャはアップバリューを独自の関数スタックにコピーして使用します。外部関数(またはクロージャ)が実行される前に、内部クロージャとその子クロージャは外部関数スタックの一部を使用します。変更を加えると、すべての関数(外部関数、クロージャ、およびそのサブクロージャ)に影響します。外部関数が実行されると、内部クロージャとそのサブクロージャが実行されます。クロージャパッケージは、外部関数からの内部クロージャのコピーを使用します。これは、最も内側のクロージャと同様に推測できます。以下に例を示します。
function f1(n)
local upvalue = "hello"
local f0 = function()
local f2 = function()
n = n * 2
print(upvalue .. " f2 " .. n)
end
local f3 = function()
print(upvalue .. " f3 " .. n)
end
print(upvalue .. " f0 " .. n)
return f2, f3
end
g1, g2 = f0()
g1()
g2()
print(upvalue .. " f1 " .. n)
return f0
end
g0 = f1(500)
$ luajit copy.lua
$ hello f0 500
$ hello f2 1000
$ hello f3 1000
$ hello f1 1000
f1は外部関数、nはその内部変数であり、内部f2とf3によって形成されるクロージャのアップバリューでもあります。f1を入力するたびに、変数nが異なり、生成されたクロージャであることに注意してください。共有されて保存されます外部関数に入るときのローカル変数です。
クロージャの役割
クロージャには主に2つの機能があります。1つは簡潔で、使用しないときにオブジェクトを生成する必要がなく、関数名も必要ありません。もう1つは、外部変数をキャプチャしてさまざまな呼び出し環境を形成できることです。名前からわかるように、クロージャはです。主な機能は後者です。つまり、外部変数をキャプチャできます。
次の例は、次のことを示しています。
function f1()
local i = 0
return function()
i = i + 1
return i
end
end
g1 = f1()
g2 = f1()
print("g1 " .. g1())
print("g1 " .. g1())
print("------------------------")
print("g2 " .. g2())
print("g2 " .. g2())
$ luajit test.lua
> g1 1
> g1 2
> ------------------------
> g2 1
> g2 2
f1が呼び出されるたびに、異なるローカル変数iが生成されます(値は関数が定義されたときの初期値です)。このとき、クロージャは他のクロージャとは関係なくiを保存して使用します。g1とg2は関数を共有します。定義upvalueの初期値ですが、呼び出し環境は独立しています。
この機能の最も一般的な使用例はイテレータです。
function f1(t)
local i = 0
return function()
i = i + 1
return t[i]
end
end
t = {"111", "heell", 5, 6, "3333"}
for e in f1(t) do
print(e)
end
$ luajit test.lua
> 111
> heell
> 5
> 6
> 3333