Achten Sie auf Ruby-Methodenaufrufe und Variablenreferenzen

Nachdruck aus Blog-Artikel.

Kürzlich habe ich ein paar Leute geschrieben, die davon abhängig sind, deshalb werde ich es für einen Moment zusammenfassen.

Ruby-Methodenaufruf

In Ruby können Sie beim Aufrufen einer Methode "die Methode aufrufen, indem Sie" () "weglassen" im Vergleich zu anderen Sprachen.

def hoge(a = nil)
  "#hoge(#{a})"
end

#Sie können eine Methode ohne Klammern aufrufen
p hoge
# => "#hoge()"

#Sie können auch bei einem methodenähnlichen Aufruf auf die Methode verweisen
p hoge()      # => "#hoge()"
p hoge 42     # => "#hoge(42)"
p self.hoge   # => "#hoge()"

Nun, das ist richtig.

Was ist, wenn ein Variablenname mit demselben Namen wie der Methodenname definiert ist?

Das Problem ist, wenn "Methoden und Variablen mit demselben Namen" gemischt werden. In diesem Fall beziehen Sie sich auf "Variable, wenn auf die Variable verwiesen werden kann" und "Andernfalls".

def hoge(a = nil)
  "#hoge(#{a})"
end

#Da zu diesem Zeitpunkt keine Variablen definiert sind, wird die Methode mit Priorität aufgerufen.
p hoge   # => #hoge()

#Variablen definieren
hoge = 42

#Geben Sie der Variablen nach dem Definieren der Variablen Priorität.
p hoge   # => 42

#Rufen Sie in einem methodenähnlichen Aufruf die Methode auf
p hoge()      # => "#hoge()"
p hoge 42     # => "#hoge(42)"
p self.hoge   # => "#hoge()"

Beachten Sie zu diesem Zeitpunkt, dass "Methode vor dem Zuweisungsausdruck" und "Variable vor dem Zuweisungsausdruck".

Wann werden die Variablen definiert?

In Ruby gibt es beispielsweise einen Fall, in dem der Prozess, der die Variable tatsächlich definiert, nicht wie folgt aufgerufen wird.

def hoge(a = nil)
  "#hoge(#{a})"
end

if false
  hoge = 42
end

#Ist das eine Methodenreferenz?
p hoge

In der Interpretersprache kann erwartet werden, dass dies "die Methode wird aufgerufen, weil die Variable nicht definiert ist". In der Realität bezieht sich "p hoge" jedoch auf die "Variable" hoge ". Dies ist ein Geheimnis dafür, wie Ruby den Quellcode ausführt. Bevor Sie in Ruby den Quellcode ausführen, "analysieren Sie zuerst den gesamten Ruby-Quellcode" und führen Sie dann den Ruby-Code aus. Wie der obige Code ist die Variable "hoge" implizit, wenn der "Zuweisungsausdruck" definiert wird, da der gesamte Quellcode analysiert wird, unabhängig davon, "ob der Code zur Laufzeit aufgerufen wird". Es wird in definiert. Unabhängig davon, ob der Inhalt der if-Anweisung tatsächlich aufgerufen wird, wird die "Variable hoge" daher als "außerhalb der if-Anweisung" bezeichnet, wenn der "Zuweisungsausdruck" definiert wird.

#Tatsächliches Ausführungsergebnis
def hoge(a = nil)
  "#hoge(#{a})"
end

if false
  hoge = 42
end

#Referenzvariablen hier
#Gibt nil zurück, da der Standardwert der Variablen nil ist
p hoge
# nil

Postfix if + Variablendefinition

Es ist sehr schwer zu verstehen, wie Ruby den Quellcode analysiert. Was passiert zum Beispiel, wenn Sie "eine" hoge "-Variable definieren, während Sie wie folgt auf" hoge "mit einem Postfix if" verweisen?

hoge = 42 if hoge.nil?
p hoge

Viele Leute denken, dass dies daran liegt, dass "if hoge.nil?" Vor der Variablendefinition verarbeitet wird, sodass der Prozess "hoge = 42" nicht tatsächlich aufgerufen wird. In Wirklichkeit wird der Code "hoge = 42 if hoge.nil?" Als "dies ist insgesamt ein Prozess" analysiert. Wenn "if hoge.nil?" Aufgerufen wird, wurde der Code "hoge = 42" bereits analysiert, und der Prozess lautet "Die Variable" hoge "ist definiert". Das Ergebnis der Ausführung des obigen Codes ist also

hoge = 42 if hoge.nil?
p hoge
# => 42

Dann wird " hoge = 42 "verarbeitet. Wenn es sich hingegen nicht um ein Postfix handelt, ist das Ergebnis anders, sodass Sie vorsichtig sein müssen.

#Dies führt zu einem Fehler
# error: undefined local variable or method `hoge' for main:Object (NameError)
if hoge.nil?
  hoge = 42
end

Dies liegt daran, dass "if hoge.nil?" Vor der Variablen analysiert wird und der bedingte Ausdruck der if-Anweisung "in dem Zustand ausgeführt wird, in dem die Variable nicht definiert ist".

Was passiert beim Auswerten ("hoge")

In Ruby gibt es eine Methode namens "eval". Dies ist die Methode, mit der der Ruby-Quellcode "zur Laufzeit" ausgeführt wird.

def hoge(a = nil)
  "#hoge(#{a})"
end

hoge = 42
#Führen Sie die an eval übergebene Zeichenfolge als Ruby-Code aus
p eval("hoge + hoge")
# => 84

Wenn der obige Code ausgeführt wird, wird nach dem Zuweisungsausdruck "hoge + hoge" ausgeführt. Siehe daher "Variable". Was passiert also, wenn Sie Code wie folgt ausführen:

def hoge(a = nil)
  "#hoge(#{a})"
end

#Führen Sie hoge vor dem Zuweisungsausdruck aus
p eval("hoge")

hoge = 42

Aus dem zuvor erläuterten Ablauf "Ich führe" "hoge" "vor der Variablen" aus, daher erwarte ich, dass dies ein "Methodenaufruf" ist. Wenn es jedoch tatsächlich ausgeführt wird, ist das Ergebnis wie folgt.

def hoge(a = nil)
  "#hoge(#{a})"
end

#Verweisen Sie auf Variablen anstelle von Methodenaufrufen
p eval("hoge")
# => nil

hoge = 42

Der Grund für dieses Ergebnis ist, dass das "Timing des Ruby-Quellcodes" und das "Timing der Ausführung von" eval "unterschiedlich sind. Der Zeitpunkt für die Ausführung von "eval" ist "zum Zeitpunkt der Ausführung von Ruby". Diese "Laufzeit" ist "nachdem der Ruby-Quellcode analysiert wurde". Mit anderen Worten, da "Timing to Execute Eval" bereits "nach dem Parsen des Ruby-Quellcodes" ist, bezieht sich der Ausdruck "Hoge" auf "Variablen zuerst". Wenn daher eine Variable oder Methode aus "eval" referenziert wird, wird die "Variable" unabhängig von der "Definitionsposition des Zuweisungsausdrucks" mit Priorität aufgerufen. Dies wirkt sich auch auf die Verwendung von "binding.irb" aus. Dies ist ein Problem, wenn "binding.irb" "vor dem Zuweisungsausdruck" aufgerufen wird. Beispiel:

def hoge(a = nil)
  "#hoge(#{a})"
end

#Bindung zum Debuggen etc..Starten Sie irb zur Laufzeit mit irb
#Wenn Sie sich auf hoge auf dieser irb-Konsole beziehen, sehen Sie "Variablen".
binding.irb

hoge = 42

binding.irb verwendet eval, um den eingegebenen Ruby-Code auszuführen. Daher wird es unter Bezugnahme auf die "Variable" wie im vorherigen Beispiel ausgeführt. Ich denke, es ist selten, "eval" in normalem Ruby-Code zu verwenden, aber Sie müssen vorsichtig sein, da die Verwendung von "binding.irb" usw. indirekt die Verwendung von "eval" bedeutet. ..

Zusammenfassung

  1. In Ruby ist nicht eindeutig, ob der Ausdruck "hoge" ein Methodenaufruf oder eine Variablenreferenz ist.
  2. Bevorzugen Sie die Variable, falls vorhanden, andernfalls priorisieren Sie den Methodenaufruf
  1. Beachten Sie jedoch, dass beim dynamischen Verweisen auf eine Variable die Variable Vorrang hat.
  1. Grundsätzlich sollten Variablennamen mit demselben Namen wie der Methodenname vermieden werden
  1. In Ruby ist es wichtig, Code zu schreiben, der sich darüber im Klaren ist, ob es sich um eine "Variable" oder eine "Methode" handelt.

Also habe ich versucht, die Ruby-Variablen zusammenzufassen. Wenn Sie tatsächlich eine Methode mit "binding.irb" aufrufen, wird möglicherweise "nil" zurückgegeben. Wenn Sie sich den Code genau ansehen, wird nach "binding.irb" ein gleichnamiger Variablenname definiert. Da war etwas. Wie Sie sehen können, gibt es in Ruby einige süchtig machende Punkte. Seien Sie also vorsichtig.

Recommended Posts

Achten Sie auf Ruby-Methodenaufrufe und Variablenreferenzen
Über Ruby-Hashes und -Symbole
Über Ruby und Objektmodell
Informationen zu Ruby-Klassen und -Instanzen
[Ruby] Fragen und Überprüfung der Anzahl der Methodenargumente
Über Ruby einfache Anführungszeichen und doppelte Anführungszeichen
[Ruby-Grundlagen] Split-Methode und to_s-Methode
Informationen zum Ruby-Produktoperator (&) und zum Summenoperator (|)
Über objektorientierte Vererbung und über Ertrag Ruby
Seien Sie vorsichtig, wenn Sie die Rückkehr in Ruby weglassen
Unterschied zwischen Ruby-Instanzvariable und lokaler Variable
[Ruby] Verwendung der gsub-Methode und der sub-Methode
Informationen zum Aufruf-Timing und zu den Argumenten der addToBackStack-Methode
Informationen zum regulären Ausdruck, der in der Ruby-Submethode verwendet wird