与 Lua 中的 debug.getlocal 混淆
Confusion with debug.getlocal in Lua
我看到 here 如何使用 Lua (5.1) 中的 debug.getlocal 函数在 table 中插入局部变量。
function locals()
local variables = {}
local idx = 1
while true do
local ln, lv = debug.getlocal(2, idx)
if ln ~= nil then
variables[ln] = lv
else
break
end
idx = 1 + idx
end
return variables
end
但是,当我尝试 return 创建的 table 并访问它的条目时,它不起作用。
function test1()
local v = 'I am a local!'
return locals()
end
print(test1().v) -- nil
经过一些尝试和错误后,我注意到在 returning 之前将 table 绑定到一个变量,或者简单地添加括号可以修复行为:
function test2()
local v = 'I am a local!'
return (locals())
end
print(test2().v) -- 'I am a local!'
这让我很困惑。为什么这两个程序有任何不同?我不明白什么? locals() 处于尾调用位置这一事实有什么不同吗?
我猜让您感到困惑的是 lua 的 proper tail call 功能。
为了理解这一点,我们修改了您的 locals
函数,使其接受一个参数作为调用 debug.getlocal
时使用的关卡堆栈。 (我正在使用 Lua 5.3.3)
-- Get local variables with stack level 'level'.
function locals(level)
local variables = {}
local idx = 1
while true do
local ln, lv = debug.getlocal(level, idx)
if ln ~= nil then
variables[ln] = lv
else
break
end
idx = 1 + idx
end
return variables
end
然后我们修改你的测试函数,添加相同的参数,并添加一个test3
函数供参考。
function test1(level)
local v = 'I am a local!'
return locals(level)
end
function test2(level)
local v = 'I am a local!'
return (locals(level))
end
function test3(level)
local v = 'I am a local!'
local a = locals(level)
return a
end
最后我们向 运行 测试添加一些代码。
local function printTable(t)
-- print(t)
for k, v in pairs(t) do
print(string.format("key = %s, val = %s.", k, v))
end
end
for level = 1, 3 do
print("==== Stack level: " .. tostring(level))
for num = 1, 3 do
print(string.format("What test%d returns: ", num))
printTable(_G[(string.format("test%d", num))](level))
print("")
end
end
上面的代码 运行 是具有不同堆栈级别的测试函数,并打印键值对 returned。我的结果如下:
==== Stack level: 1
What test1 returns:
key = variables, val = table: 0x7fa14bc081e0.
key = idx, val = 3.
key = level, val = 1.
What test2 returns:
key = variables, val = table: 0x7fa14bc08220.
key = idx, val = 3.
key = level, val = 1.
What test3 returns:
key = variables, val = table: 0x7fa14bc088b0.
key = idx, val = 3.
key = level, val = 1.
==== Stack level: 2
What test1 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 1.
key = level, val = 2.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 1.
What test2 returns:
key = level, val = 2.
key = v, val = I am a local!.
What test3 returns:
key = level, val = 2.
key = v, val = I am a local!.
==== Stack level: 3
What test1 returns:
key = (*temporary), val = function: 0x109f5a070.
What test2 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 2.
key = level, val = 3.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 2.
What test3 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 3.
key = level, val = 3.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 3.
当level
为1时,locals
很好地赋予了它自己的局部变量。但是当 level
为 2 时,test1
returns 是外部范围的变量,而 test2
和 test3
给出了您期望的结果。对于堆栈级别 3 test2
和 test3
return 类似于堆栈级别 2 的 test1
。所以看起来 test1
skips 堆栈级别,我能想到的唯一解释是正确的尾调用。
根据PIL(我在开头提供的link),适当的尾部调用永远不会导致堆栈溢出,我认为这是在一些内联 方式。如果我是对的,这就解释了 test1
的 return 语句的跳过行为,因为这是一个正确的尾调用,并且是 3 个测试函数中唯一的一个。
我看到 here 如何使用 Lua (5.1) 中的 debug.getlocal 函数在 table 中插入局部变量。
function locals()
local variables = {}
local idx = 1
while true do
local ln, lv = debug.getlocal(2, idx)
if ln ~= nil then
variables[ln] = lv
else
break
end
idx = 1 + idx
end
return variables
end
但是,当我尝试 return 创建的 table 并访问它的条目时,它不起作用。
function test1()
local v = 'I am a local!'
return locals()
end
print(test1().v) -- nil
经过一些尝试和错误后,我注意到在 returning 之前将 table 绑定到一个变量,或者简单地添加括号可以修复行为:
function test2()
local v = 'I am a local!'
return (locals())
end
print(test2().v) -- 'I am a local!'
这让我很困惑。为什么这两个程序有任何不同?我不明白什么? locals() 处于尾调用位置这一事实有什么不同吗?
我猜让您感到困惑的是 lua 的 proper tail call 功能。
为了理解这一点,我们修改了您的 locals
函数,使其接受一个参数作为调用 debug.getlocal
时使用的关卡堆栈。 (我正在使用 Lua 5.3.3)
-- Get local variables with stack level 'level'.
function locals(level)
local variables = {}
local idx = 1
while true do
local ln, lv = debug.getlocal(level, idx)
if ln ~= nil then
variables[ln] = lv
else
break
end
idx = 1 + idx
end
return variables
end
然后我们修改你的测试函数,添加相同的参数,并添加一个test3
函数供参考。
function test1(level)
local v = 'I am a local!'
return locals(level)
end
function test2(level)
local v = 'I am a local!'
return (locals(level))
end
function test3(level)
local v = 'I am a local!'
local a = locals(level)
return a
end
最后我们向 运行 测试添加一些代码。
local function printTable(t)
-- print(t)
for k, v in pairs(t) do
print(string.format("key = %s, val = %s.", k, v))
end
end
for level = 1, 3 do
print("==== Stack level: " .. tostring(level))
for num = 1, 3 do
print(string.format("What test%d returns: ", num))
printTable(_G[(string.format("test%d", num))](level))
print("")
end
end
上面的代码 运行 是具有不同堆栈级别的测试函数,并打印键值对 returned。我的结果如下:
==== Stack level: 1
What test1 returns:
key = variables, val = table: 0x7fa14bc081e0.
key = idx, val = 3.
key = level, val = 1.
What test2 returns:
key = variables, val = table: 0x7fa14bc08220.
key = idx, val = 3.
key = level, val = 1.
What test3 returns:
key = variables, val = table: 0x7fa14bc088b0.
key = idx, val = 3.
key = level, val = 1.
==== Stack level: 2
What test1 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 1.
key = level, val = 2.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 1.
What test2 returns:
key = level, val = 2.
key = v, val = I am a local!.
What test3 returns:
key = level, val = 2.
key = v, val = I am a local!.
==== Stack level: 3
What test1 returns:
key = (*temporary), val = function: 0x109f5a070.
What test2 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 2.
key = level, val = 3.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 2.
What test3 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 3.
key = level, val = 3.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 3.
当level
为1时,locals
很好地赋予了它自己的局部变量。但是当 level
为 2 时,test1
returns 是外部范围的变量,而 test2
和 test3
给出了您期望的结果。对于堆栈级别 3 test2
和 test3
return 类似于堆栈级别 2 的 test1
。所以看起来 test1
skips 堆栈级别,我能想到的唯一解释是正确的尾调用。
根据PIL(我在开头提供的link),适当的尾部调用永远不会导致堆栈溢出,我认为这是在一些内联 方式。如果我是对的,这就解释了 test1
的 return 语句的跳过行为,因为这是一个正确的尾调用,并且是 3 个测试函数中唯一的一个。