2012-05-04 16 views
7

Ich habe den Eindruck, dass Klassendefinitionen in Ruby wieder geöffnet werden können:Lokale Variablen in Klassendefinitionen/Umfang

class C 
    def x 
    puts 'x' 
    end 
end 

class C 
    def y 
    puts 'y' 
    end 
end 

Dies funktioniert wie erwartet und y in der ursprünglichen Klassendefinition hinzugefügt.

Ich bin verwirrt, warum der folgende Code funktioniert nicht wie erwartet:

class D 
    x = 12 
end 

class D 
    puts x 
end 

Dies in einer NameError Ausnahme führen. Warum wird ein neuer lokaler Bereich gestartet, wenn eine Klasse wieder geöffnet wird? Dies scheint ein wenig kontraintuitiv. Gibt es eine Möglichkeit, den vorherigen lokalen Bereich fortzusetzen, wenn eine Klasse erweitert wird?

+0

Dies ist eine gute Frage, auf seine eigene, aber gibt es einen Grund, warum Sie wollen tue dies, anstatt eine Instanzvariable auf dem Klassenobjekt selbst zu speichern (dh "x" durch "@ x" oben ersetzen)? – Phrogz

+0

@Phrogz Nein, es gibt keinen Grund, warum ich das im Produktionscode machen möchte. Ich bitte darum, ein intellektuelles Problem zu kratzen, eher um ein Problem der realen Welt zu lösen. – Matty

Antwort

7

Lokale Variablen werden nicht mit einem Objekt zugeordnet ist, werden sie mit einem Umfang zugeordnet.

Lokale Variablen sind lexikalisch begrenzt. Was Sie versuchen, ist nicht mehr gültig zu tun, als:

def foo 
    x = :hack if false # Ensure that x is a local variable 
    p x if $set   # Won't be called the first time through 
    $set = x = 42  # Set the local variable and a global flag 
    p :done 
end 

foo     #=> :done 

foo     #=> nil (x does not have the value from before) 
         #=> done 

In den obigen Ausführungen ist das gleiche Verfahren, auf das gleiche Objekt, ist, dass beide Male ausgeführt wird. Die self ist unverändert. Die lokalen Variablen werden jedoch zwischen den Aufrufen gelöscht.

Das erneute Öffnen der Klasse ähnelt dem erneuten Aufruf einer Methode: Sie befinden sich im selben Bereich wie self, aber Sie beginnen einen neuen lokalen Kontext. Wenn Sie den class D-Block mit end schließen, werden Ihre lokalen Variablen verworfen (es sei denn, sie waren closed over).

6

In Ruby sind lokale Variablen nur in dem Bereich verfügbar, in dem sie definiert sind. Die Schlüsselwörter class verursachen jedoch einen völlig neuen Bereich.

class D 
    # One scope 
    x = 12 
end 

class D 
    # Another scope 
    puts x 
end 

So können Sie keinen Zugriff auf lokale Variable erhalten, die in dem ersten class Abschnitt definiert, weil, wenn Sie zuerst Umfang verlassen, lokale Variable im Innern zerstört und Speicher wird von der Garbage Collection freigegeben. Dies gilt auch für die def, zum Beispiel.

+2

Ist das nicht nur die Frage neu? Ja, es ist ein anderer Bereich, das OP hat das erkannt. Aber warum? – AShelly

+1

Danke, ich habe eine Ergänzung zu meiner Antwort hinzugefügt. – Flexoid

0

Die einfache Lösung wäre x ein class instance variable. Natürlich beantwortet das nicht Ihre Bereichsfrage. Ich glaube, der Grund x ist nicht in der scope (siehe Nachweis der Geltungsbereich eines Ruby-Variable) der Wiedereröffnung Klasse wird, weil sein Umfang nie , dass der Klasse war D an erster Stelle. Der Bereich x wurde beendet, wenn die Klasse D geschlossen ist, weil x weder eine Instanzvariable noch eine Klassenvariable ist.

Ich hoffe, dass hilft.

+0

Nicht wahr. Probiere 'x = 42; Klasse Foo; x = 17; p x; Ende; p x' und beachten Sie, dass Sie '17' und dann' 42' sehen. Das Öffnen einer Klasse startet einen neuen lokalen Kontext. – Phrogz

0

Ein Teil der Gründe, warum dieses Verhalten sinnvoll ist, liegt in den Metaprogrammierungsfunktionen. Sie könnten mit einigen temporären Variablen sein, Daten zu speichern, dass Sie eine neue Konstante zu nennen verwenden könnten, oder einen Methodennamen zu verweisen:

class A 
    names, values = get_some_names_and_values() 

    names.each_with_index do |name, idx| 
    const_set name, value[idx] 
    end 
end 

Oder vielleicht müssen Sie die Eigenklasse bekommen ...

class B 
    eigenclass = class << self; self; end 
    eigenclass.class_eval do 
    ... 
    end 
end 

es Sinn machen, einen neuen Bereich eine Klasse wieder öffnen jedes Mal zu haben, denn in Ruby oft Sie Code innerhalb einer Klasse definiton schreiben, die tatsächliche Code ausgeführt und Öffnung eine Klasse zu sein ist nur ein Weg, um der richtige Wert für self. Sie wollen nicht den Inhalt der Klasse durch diese Variablen verunreinigt werden, otherwhise Sie eine Instanz der Klasse Variable verwenden würde:

class C 
    @answer = 42 
end 

class C 
    p @answer 
end