2016-06-11 8 views
1

Ist es in Ruby möglich, alle innerhalb einer Methode definierten lokalen Variablennamen von außerhalb der Methode mithilfe von Metaprogrammierung zu erhalten? Lokale Variablennamen innerhalb einer Methode von außerhalb der Methode ermitteln

def foo 
    var = 100 
    arr = [1,2] 
end 

So etwas wie foo.local_variables.

Ich brauche nur die Variablennamen, nicht ihre Werte abrufen.

Warum ich versuche, diese Variablennamen zu finden: Ich habe eine Klasse, wo ich Setter für alle Instanzvariablen dynamisch generieren möchte initialisiert in "#initialize".

Damit innen „initialisieren“ zu tun, ich mag etwas bin mit instance_variables.each do |inst_var_name| define_singleton_method(...) do ...

Dies funktioniert: Jedes instanzierte Objekt erhält seinen Setter als Singleton-Methoden. Ich suchte jedoch nach einer Lösung, bei der ich die Namen der Instanzvariablen von außerhalb der "initialize" -Methode lesen konnte, um "#define_method" für sie verwenden zu können und um normale Methoden und keine Singleton-Methoden zu erstellen.

+0

Nein, das ist nicht möglich. Im Gegensatz zu Instanzvariablen, die so lange existieren, wie das Objekt existiert, sind lokale Variablen nicht vorhanden, bis die Methode aufgerufen wird und nicht mehr existiert, sobald die Methode zurückkehrt. Es gibt kein "innerhalb der Methode" und "außerhalb der Methode"; Es gibt nur "während die Methode ausgeführt wird" und "während die Methode nicht ausgeführt wird". Welches Problem versuchen Sie zu lösen? –

+0

Wenn es möglich ist, wie viele Metadaten Ruby zur Laufzeit halten soll? Wie viel Speicher werden sie einnehmen? Es ist einfach nicht machbar. – Aetherus

+0

@ Jordan stimme ich nicht zu. Ich versuche nicht, den Wert von lokalen Variablen zu erhalten, nur ihren Namen. Da es möglich ist, den Quellcode einer Methode zu bekommen, habe ich mich gefragt, ob es auch eine Methode gibt, die Namen nur der lokalen Variablen zu erhalten, die in einer Methode definiert sind (oder versucht werden sollen). –

Antwort

1

Sie können die Methode (re) parsen und den S-EXPR-Baum untersuchen. Siehe unten für einen Proof of Concept. Sie können die Datei, in der die Methode definiert ist, mit Method#source_location aufrufen und dann diese Datei lesen. Es gibt sicherlich Raum für Verbesserungen an meinem Code, aber sollten Sie beginnen. Es ist ein voll funktionsfähiges Stück Code und erfordert nur den Rubin-Parser-Edelstein (https://github.com/whitequark/parser).

require 'parser/current' 
node = Parser::CurrentRuby.parse(DATA.read) # DATA is everything that comes after __END__ 

def find_definition(node, name) 
    return node if definition_node?(node, name) 
    if node.respond_to?(:children) 
    node.children.find do |child| 
     find_definition(child, name) 
    end 
    end 
end 

def definition_node?(node, name) 
    return false if !node.respond_to?(:type) 
    node.type == :def && node.children.first == name 
end 

def collect_lvasgn(node) 
    result = [] 
    return result if !node.respond_to?(:children) 

    node.children.each do |child| 
    if child.respond_to?(:type) && child.type == :lvasgn 
     result << child.children.first 
    else 
     result += collect_lvasgn(child) 
    end 
    end 

    result 
end 

definition = find_definition(node, :foo) 
puts collect_lvasgn(definition) 

__END__ 

def foo 
    var = 100 
    arr = [1,2] 
    if something 
    this = 3 * var 
    end 
end 

def bar 
    var = 200 
    arr = [3, 4] 
end 

Haben Sie etwas dagegen, uns zu sagen, WARUM Sie die Variablen finden möchten?

+0

Hallo, ich habe einige verwandte Informationen zu der Frage hinzugefügt, danke. –

+0

Danke für das Update. Ich bin mir nicht sicher, ob ich Ihre Anforderungen vollständig verstanden habe. Aber es klingt wie ein hackischer Weg, etwas zu tun. Wenn es so kompliziert ist, dann ist es wahrscheinlich nicht sehr idiomatisch (obwohl Sie es tun können). Auch mein Weg hat wahrscheinlich Schwächen, wenn es um dynamisch erstellte Sachen geht. –

+0

Ja, ich weiß, dass es sich zu hetzisch anfühlt, aber es ist eine jener Zeiten, in denen du die Gelegenheit hast, etwas Neues zu lernen, also treibe es weiter! :) Das eigentliche Problem ist nicht mehr so ​​wichtig ... und ich hoffe wirklich, dass es einen anderen Ansatz gibt. Ich werde wahrscheinlich eine Frage stellen, nur um das zu diskutieren. –

0

Diese Variablen existieren nur kurz, da Jordan darauf hinweist, dass sie nur so lange existieren, wie die Methode aktiv ausgeführt wird und unmittelbar danach verworfen wird.

Wenn Sie diese instrumentieren möchten, müssen Sie dies innerhalb der Methode tun. Die Methode selbst erlaubt diese Art der Reflexion nicht.

+0

Bitte schauen Sie sich meinen Kommentar als Antwort auf @Jordan unter meiner Frage an. –

+0

Wenn Sie sich sehr mutig fühlen, sollten Sie vielleicht versuchen, den internen Ruby-P-Code auszugeben und zu parsen, aber ich denke, das, wonach Sie fragen, wird von normalem Ruby überhaupt nicht unterstützt. – tadman

+1

Tatsächlich werden in jedem halbwegs ordentlichen Compiler (z. B. Rubinius, JRuby, IronRuby) die lokalen Variablen in diesem Beispiel vollständig wegoptimiert (wie auch die gesamte erste Zeile). –

Verwandte Themen