Warum kann ich nicht auf eine lokale Variable innerhalb einer Methode in Ruby zugreifen?

8

Ich habe eine Ruby-Datei namens test.rb

%Vor%

Ich führe es aus, habe einen Fehler:

test.rb:3:in 'test': undefined local variable or method 'ff' for main:Object (NameError)

Was ist der Grund dafür? Gibt es Unterlagen, um das zu erklären?

    
qichunren 28.05.2012, 03:53
quelle

4 Antworten

12

Der Grund dafür, dass ff in der test -Methodendefinition nicht verfügbar ist, besteht einfach darin, dass Methoden (die mit dem Schlüsselwort def erstellt wurden) einen neuen Bereich erstellen. Gleiches gilt für das Definieren von Klassen und Modulen mit den Schlüsselwörtern class bzw. module .

Die Rolle von main (das Objekt der obersten Ebene) ist in dieser Situation fast völlig irrelevant für die Frage des Geltungsbereichs.

Wenn Sie möchten, dass Ihre Methode test Zugriff auf im Definitionskontext definierte Locals hat, verwenden Sie die Methode define_method (oder in Ihrem Fall die Methode define_singleton_method ), siehe hier:

%Vor%

Im Gegensatz zum Schlüsselwort def erstellt die Methodenfamilie define_method keine neuen Bereiche, sondern schließt über den aktuellen Bereich und erfasst alle lokalen Variablen.

Der Grund dafür, dass @ff im nächsten Beispiel von @soup verwendet wurde, ist nicht, dass main irgendwie ein "Spezialfall" ist, sondern dass ein auf oberster Ebene definierter ivar ein ivar von main und ist Dies ist für eine Methode zugänglich, die auf main aufgerufen wird.

Was ist jedoch die Beziehung der Methode test zu main ? Es ist keine Methode on nur main selbst - es ist tatsächlich eine private Instanzmethode, die in der Klasse Object definiert ist. Dies bedeutet, dass die Methode test für fast jedes Objekt in Ihrem Ruby-Programm als private Methode verfügbar wäre. Alle auf der obersten Ebene definierten Methoden ( main ) sind tatsächlich als private Instanzmethoden in der Klasse Object definiert.

Weitere Informationen zur obersten Ruby-Ebene finden Sie in diesem Artikel: Ссылка

    
horseyguy 28.05.2012, 10:11
quelle
9

Der Ruby-Bereich ist einfach und komplex.

Zuerst müssen Sie daran denken, dass alles ein Objekt ist und alles einen Bereich hat.

Um Ihre Frage direkt zu beantworten, ist main ein Objekt und wenn Sie def x... schreiben, definieren Sie eine Methode für das Objekt main

Beobachten Sie in pyr / irb:

%Vor%

Also wenn du schreibst

%Vor%

Was du wirklich tust, ist

%Vor%

Was nicht funktioniert, weil ff den Gültigkeitsbereich nicht überschreitet. Um dies zu beheben, musst du ff in eine Instanzvariable umwandeln, also setze den Namen mit @ voran. Folgendes wird funktionieren:

%Vor%

Beachten Sie, dass dies ein Sonderfall für main als reguläre Klassen zu sein scheint, siehe unten.

Wenn wir eine eigene Testklasse haben:

%Vor%

Dies schlägt fehl, weil ff nicht wie erwartet im richtigen Bereich definiert ist. Es wird stattdessen darauf beschränkt, wenn der Parser unsere Klassendeklaration ausführt.

Also versuchen wir es mit dem obigen Fix:

%Vor%

Das ist seltsam.

Lässt diese Methode hinzufügen:

%Vor%

Warum ist @ff nil?

Folgendes könnte dies verdeutlichen:

%Vor%

Wenn wir das ausführen, erhalten wir folgende Ausgabe:

%Vor%

Wie Sie sehen können, läuft der Interpreter genau wie anderswo über unsere Klassendeklaration in der Reihenfolge und gibt unsere puts -Anweisungen aus. Das wird interessanter, wenn wir Methoden aufrufen. Das Schreiben von @instance = ... ist das gleiche wie das Schreiben von self.instance = ... , also können Sie raten, wo wir die Instanz definiert haben? Ja, auf dem Klassenobjekt Test2 (kein Test2-Objekt).

Das ist der Grund, warum wir bei Aufruf von test nichts ausgeben, weil test , self sich auf das instanziierte Test2 -Objekt bezieht, nicht Test2 class object (wo wir @instance setzen) etwas!). Aus diesem Grund richten Sie Ihre Instanzvariablen in initialize ein, wobei self auf das tatsächliche Objekt zeigt.

Sie können sehen, wenn wir eine Klassenmethode über self.class_test definieren und dann diese Methode für das Klassenobjekt Test2 ( Test2.class_test ) aufrufen, erhalten wir die erwartete Ausgabe, weil in diesem Bereich @instance definiert wurde.

Ich hoffe, das hat einen Sinn ergeben.

Der Bereich Ссылка kann helfen, etwas von diesem Wissen zu festigen.

    
Soup 28.05.2012 04:45
quelle
2

In Ruby hat ein Name ohne Sigil immer "lokalen" Geltungsbereich. Ruby verwendet lexikalisches Scoping, also bezieht sich der Name innerhalb einer Methode immer nur auf diese Methode.

Die anderen Bereiche sind global, Instanz und Klasse. Hier ist ein gutes Dokument über sie:   Ссылка

Es gibt einige Möglichkeiten, Ihr Problem zu lösen. Mein Favorit wäre, das als Argument gedruckte Ding weiterzugeben:

%Vor%

Wahrscheinlicher ist es, dass Sie das kapseln, was Sie in einer Klasse tun und eine Instanzvariable verwenden.

Sie könnten auch einfach eine globale Variable ($ ff) verwenden, aber auf diese Weise liegt Wahnsinn.

    
Tim Peters 28.05.2012 04:49
quelle
0
  

Warum kann ich nicht auf eine lokale Variable innerhalb einer Methode in Ruby zugreifen?

Weil es eine lokale Variable ist. Lokale Variablen sind lokal für den Bereich, in dem sie definiert sind. Deshalb werden sie schließlich als "lokale Variablen" bezeichnet.

In diesem Fall haben Sie im Skriptbereich eine lokale Variable definiert, die im Skriptbereich und nirgendwo anders sichtbar ist.

    
Jörg W Mittag 28.05.2012 12:04
quelle

Tags und Links