2016-08-06 55 views
3

Ich sah here wie lokale Variablen in einer Tabelle mithilfe der debug.getlocal -Funktion in Lua (5.1) eingefügt werden.Verwechslung mit debug.getlocal in 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 

Wenn ich jedoch versuche, die erstellte Tabelle zurückzugeben und auf ihre Einträge zuzugreifen, funktioniert es nicht.

function test1() 
    local v = 'I am a local!' 
    return locals() 
end 

print(test1().v) -- nil 

Nach einigem Pfad und Fehlern, bemerkte ich, dass vor der Rückkehr in die Tabelle in einem variable Bindung oder einfaches Hinzufügen von Klammer das Verhalten behebt:

function test2() 
    local v = 'I am a local!' 
    return (locals()) 
end 

print(test2().v) -- 'I am a local!' 

Dies ist mir sehr verwirrend. Warum unterscheiden sich diese beiden Programme in irgendeiner Weise? Was verstehe ich nicht? Hat die Tatsache, dass Locals() in einer Tail-Call-Position ist, einen Unterschied?

Antwort

2

Ich denke, was verwirrt Sie ist die proper tail call Funktion von Lua.

Um dies zu verstehen, ändern wir Ihre locals Funktion, so dass es ein Argument als Level-Stack akzeptiert in Aufruf an debug.getlocal. (Ich bin mit 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 

Dann wir Ihre Testfunktionen ändern, das Hinzufügen das gleiche Argument, und fügen Sie eine test3 Funktion als Referenz.

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 

Schließlich fügen wir etwas Code hinzu, um die Tests durchzuführen.

Der obige Code führt die Testfunktionen mit verschiedenen Stack-Ebenen aus und gibt die zurückgegebenen Schlüssel/Wert-Paare aus. Mein Ergebnis ist wie folgt:

==== 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. 

Wenn level 1, locals funktioniert gut seine eigene lokale Variablen zu geben. Aber wenn level 2 ist, gibt test1 Variablen des äußeren Bereichs zurück, während test2 und test3 das Ergebnis ergeben, das Sie erwarten. Für Stack-Level 3 test2 und test3 zurück etwas wie test1 auf Stack-Ebene 2. So scheint es test1springt ein Stack-Level, und die einzige Erklärung, die ich denken könnte, ist die richtige Tail Call.

Nach PIL (der Link, den ich am Anfang), ein richtiger Tail-Call wird nie dazu führen, dass der Stapel überlaufen, die ich als den Anruf in einigen inline Weg nehmen. Wenn ich damit recht habe, erklärt dies das Überspringungsverhalten der Return-Anweisung test1, denn das ist ein richtiger Tail-Call und der einzige in den 3 Testfunktionen.

+0

Ich liebe Ihre Methodik! Ich danke dir sehr. – yawn

+1

Ich bin mir nicht sicher, wie man Code in Kommentaren schreibt, aber es scheint tatsächlich über Tail Calls zu sein, da das Wrapping von Locals (2) mit einer Identity Funktion funktioniert, die, wie in Schema vorausgesetzt, ausreichen sollte Tail-Call-Optimierung. Ich nehme an, dass, da Klammern in Lua irgendeine Form der Berechnung ausführen (nur den ersten von mehreren Werten zurückgeben), sie ausreichend gewesen sein müssen, um den TCO zu deaktivieren – yawn