我看到 here如何使用Lua(5.1)中的debug.getlocal函数在表中插入局部变量. 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 brea
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
但是,当我尝试返回创建的表并访问它的条目时,它不起作用.
function test1() local v = 'I am a local!' return locals() end print(test1().v) -- nil
在一些跟踪和错误之后,我注意到在返回之前将表绑定到变量,或者只是添加括号来修复行为:
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
上面的代码运行具有不同堆栈级别的测试函数,并打印返回的键值对.我的结果如下:
==== 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返回外部作用域的变量,而test2和test3给出你期望的结果.对于堆栈级别3,test2和test3在堆栈级别2返回类似test1的内容.因此,似乎test1跳过堆栈级别,我能想到的唯一解释是正确的尾部调用.
根据PIL(我在开头提供的链接),正确的尾调用永远不会导致堆栈溢出,我采用一些内联方式进行调用.如果我是对的,这解释了test1的return语句的跳过行为,因为这是一个正确的尾调用,并且是3个测试函数中唯一的一个.