Step By Step(Lua環境)
11496 ワード
Luaはそのすべてのグローバル変数を通常のtableに保存し、このtableを「環境」と呼ぶ.グローバル変数に保存されます.G中. 1. グローバル変数宣言:Luaのグローバル変数は宣言を必要とせずに使用できます.便利ですが、誤筆が発生すると発見しにくい間違いになります.私たちは_Gテーブルにメタテーブルを付けることで、グローバル変数の読み取りと設定を保護し、このような誤記問題の発生確率を低減することができます.次のコードの例を示します.
2. 非グローバル環境:グローバル環境には、プログラムのすべての部分に変更が影響するという剛性の問題があります.Lua 5はこのためにいくつかの改良を行い、新しい特徴は各関数が独自のグローバル環境を持つことをサポートすることができ、この関数によって作成されたclosure関数は関数のグローバル変数テーブルを継承する.ここではsetfenv関数によって1つの関数の環境を変更することができ、この関数は2つのパラメータを受け入れ、1つは関数名であり、もう1つは新しい環境tableである.最初のパラメータは、関数名自体に加えて、現在の関数呼び出しスタック内のレイヤ数を表す数値として指定できます.数字1は現在の関数を表し、2は呼び出し関数を表します.次のコードを参照してください.
どうしてこのような結果を得たのですか.printは変数aと同様にグローバルテーブルのフィールドであり、新しいグローバルテーブルは空であるため、print呼び出しはエラーを報告する.この副作用に対応するために、従来のグローバルテーブルをGは、新しいグローバルテーブルの内部テーブルとして、既存のグローバル変数にアクセスする際に、直接_に移動することができるGのフィールドは、新しいグローバルフィールドについては、新しいグローバルテーブルに保持されます.これにより,関数における誤修正であっても,グローバル変数(_G)が用いられる他の場所には影響しない.次のコードを参照してください.
最後に示す例は、関数環境変数の継承です.次のコードを参照してください.
1 -- table
2 local declaredNames = {}
3 local mt = {
4 __newindex = function(table,name,value)
5 -- , , rawset 。
6 if not declaredNames[name] then
7 -- C , , , 。
8 local w = debug.getinfo(2,"S").what
9 if w ~= "main" and w ~= "C" then
10 error("attempt to write to undeclared variable " .. name)
11 end
12 -- , declaredNames , 。
13 declaredNames[name] = true
14 end
15 print("Setting " .. name .. " to " .. value)
16 rawset(table,name,value)
17 end,
18
19 __index = function(_,name)
20 if not declaredNames[name] then
21 error("attempt to read undeclared variable " .. name)
22 else
23 return rawget(_,name)
24 end
25 end
26 }
27 setmetatable(_G,mt)
28
29 a = 11
30 local kk = aa
31
32 -- :
33 --[[
34 Setting a to 11
35 lua: d:/test.lua:21: attempt to read undeclared variable aa
36 stack traceback:
37 [C]: in function 'error'
38 d:/test.lua:21: in function <d:/test.lua:19>
39 d:/test.lua:30: in main chunk
40 [C]: ?
41 --]]
2. 非グローバル環境:グローバル環境には、プログラムのすべての部分に変更が影響するという剛性の問題があります.Lua 5はこのためにいくつかの改良を行い、新しい特徴は各関数が独自のグローバル環境を持つことをサポートすることができ、この関数によって作成されたclosure関数は関数のグローバル変数テーブルを継承する.ここではsetfenv関数によって1つの関数の環境を変更することができ、この関数は2つのパラメータを受け入れ、1つは関数名であり、もう1つは新しい環境tableである.最初のパラメータは、関数名自体に加えて、現在の関数呼び出しスタック内のレイヤ数を表す数値として指定できます.数字1は現在の関数を表し、2は呼び出し関数を表します.次のコードを参照してください.
1 a = 1
2 setfenv(1,{})
3 print(a)
4
5 -- :
6 --[[
7 lua: d:/test.lua:3: attempt to call global 'print' (a nil value)
8 stack traceback:
9 d:/test.lua:3: in main chunk
10 [C]: ?
11 --]]
どうしてこのような結果を得たのですか.printは変数aと同様にグローバルテーブルのフィールドであり、新しいグローバルテーブルは空であるため、print呼び出しはエラーを報告する.この副作用に対応するために、従来のグローバルテーブルをGは、新しいグローバルテーブルの内部テーブルとして、既存のグローバル変数にアクセスする際に、直接_に移動することができるGのフィールドは、新しいグローバルフィールドについては、新しいグローバルテーブルに保持されます.これにより,関数における誤修正であっても,グローバル変数(_G)が用いられる他の場所には影響しない.次のコードを参照してください.
1 a = 1
2 local newgt = {} --
3 setmetatable(newgt,{__index = _G})
4 setfenv(1,newgt)
5 print(a) -- 1
6
7 a = 10
8 print(a) -- 10
9 print(_G.a) -- 1
10 _G.a = 20
11 print(a) -- 10
最後に示す例は、関数環境変数の継承です.次のコードを参照してください.
1 function factory()
2 return function() return a end
3 end
4 a = 3
5 f1 = factory()
6 f2 = factory()
7 print(f1()) -- 3
8 print(f2()) -- 3
9
10 setfenv(f1,{a = 10})
11 print(f1()) -- 10
12 print(f2()) -- 3