2016-08-06 2 views
3

Я видел here, как вставлять локальные переменные в таблицу, используя функцию debug.getlocal в Lua (5.1).Путаница с debug.getlocal в Lua

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() находится в позиции вызова хвоста, имеет значение?

ответ

2

Я думаю, что вас смущает функция proper tail call lua.

Чтобы понять это, мы изменяем вашу функцию 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 верните что-то вроде test1 на уровне стека 2. Таким образом, кажется, test1пропускает уровень стека, и единственным объяснением, о котором я мог думать, является правильный хвостовой вызов.

В соответствии с PIL (ссылка, которую я предоставляю в начале) правильный хвостовой вызов никогда не вызовет переполнение стека, которое я принимаю при выполнении вызова в некотором встроенных способах. Если я прав, это объясняет пропущенное поведение оператора возврата test1, потому что это правильный хвостовой вызов и единственный в трех тестовых функциях.

+0

Мне нравится ваша методология! Огромное спасибо. – yawn

+1

Я не уверен, как отправлять комментарии в комментариях, но, похоже, это скорее вызовы хвоста, так как обертывание locals (2) с помощью функции идентификации работает, что, если предположить, что он работает, как в Scheme, должно быть достаточно, чтобы отключить оптимизация хвостовых вызовов. Я предполагаю, что, поскольку круглые скобки выполняют некоторую форму вычислений в Lua (возвращают только первое из нескольких значений), то их должно быть достаточно, чтобы отключить TCO – yawn