Was bedeutet der folgende Code in Ruby?
||=
Hat es eine Bedeutung oder einen Grund für die Syntax?
Diese Frage wurde in den Ruby-Mailinglisten und Ruby-Blogs so oft diskutiert, dass es jetzt sogar Threads in der Ruby-Mailingliste gibt, deren einziger Zweck darin besteht, Links zu allen anderen Threads in der Ruby-Mailingliste zu sammeln , die dieses Problem behandeln .
Hier ist eine: Die endgültige Liste von || = (OR Equal) Threads und Seiten
Wenn Sie wirklich wissen möchten, was los ist, lesen Sie Abschnitt 11.4.2.3 "Abgekürzte Zuweisungen" der Ruby Language Draft Specification .
In erster Näherung
a ||= b
ist äquivalent zu
a || a = b
und nicht gleichbedeutend mit
a = a || b
Dies ist jedoch nur eine erste Annäherung, insbesondere wenn sie nicht a
definiert ist. Die Semantik hängt auch davon ab, ob es sich um eine einfache Variablenzuweisung, eine Methodenzuweisung oder eine Indexzuweisung handelt:
a ||= b
a.c ||= b
a[c] ||= b
werden alle unterschiedlich behandelt.
a = false; a ||= true
die nicht das tut, was Ihre Antwort sagt, nicht als "Nuance" bezeichnen.a ||= b
ist ein bedingter Zuweisungsoperator . Es bedeutet, wenna
undefiniert oder falsch ist , dann bewertenb
unda
auf das Ergebnis setzen . Entsprechend wird, wenna
es definiert ist und als wahr bewertet wird,b
es nicht bewertet und es findet keine Zuordnung statt. Zum Beispiel:Verwirrenderweise sieht es anderen Zuweisungsoperatoren (z. B.
+=
) ähnlich , verhält sich jedoch anders.a += b
wird übersetzt ina = a + b
a ||= b
grob übersetzt zua || a = b
Es ist eine Abkürzung für
a || a = b
. Der Unterschied ist, dass, wenna
undefiniert ist,a || a = b
erhöhen würdeNameError
, währenda ||= b
setzta
aufb
. Diese Unterscheidung ist unwichtig , oba
undb
beiden lokalen Variablen sind, ist jedoch von Bedeutung , wenn entweder ein Getter / Setter - Methode einer Klasse ist.Weiterführende Literatur:
quelle
h = Hash.new(0); h[1] ||= 2
. Betrachten wir nun die beiden möglichen Erweiterungenh[1] = h[1] || 2
vsh[1] || h[1] = 2
. Beide Ausdrücke werden ausgewertet,0
aber der erste erhöht die Größe des Hashs unnötig. Vielleicht hat Matz deshalb beschlossen,||=
sich eher wie die zweite Erweiterung zu verhalten. (Ich habe dies auf ein Beispiel aus einem der Threads gestützt, auf die in einer anderen Antworta || a = b
wirft einNameError
Wenna
ist undefiniert.a ||= b
nicht, sondern initialisierta
und setzt es aufb
. Das ist meines Wissens der einzige Unterschied zwischen den beiden. Ebenso ist der einzige Unterschied zwischena = a || b
unda ||= b
mir bewusst, dassa=
eine Methode aufgerufen wird, unabhängig davon, wasa
zurückgegeben wird. Der einzige Unterschied zwischena = b unless a
unda ||= b
mir ist bewusst, dass diese Aussage bewertet wird,nil
anstatta
ob siea
wahr ist. Viele Annäherungen, aber nichts ganz Äquivalentes ...Prägnante und vollständige Antwort
wertet genauso aus wie jede der folgenden Zeilen
- -
Auf der anderen Seite,
wertet genauso aus wie jede der folgenden Zeilen
- -
Bearbeiten: Wie AJedi32 in den Kommentaren ausgeführt hat, gilt dies nur, wenn: 1. a eine definierte Variable ist. 2. Eine einmalige und zweimalige Auswertung führt nicht zu einem Unterschied im Programm- oder Systemstatus.
quelle
a
false / zero / undefined ist, es zweimal ausgewertet wird. (Aber ich kenne Ruby nicht, also weiß ich nicht, ob lWerte genau 'ausgewertet' werden können ...)a || a = b
,a ? a : a = b
,if a then a else a = b end
, Undif a then a = a else a = b end
wirft einen Fehler , wenna
nicht definiert ist, währenda ||= b
unda = a || b
wird nicht. Aucha || a = b
,a ? a : a = b
,if a then a else a = b end
,a = a ? a : b
, undif a then a = a else a = b end
bewertena
zweimal , wenna
truthy ist, währenda ||= b
unda = a || b
dies nicht tun.a || a = b
Wird nichta
zweimal ausgewertet, wenn diesa
zutrifft.the end state will be equivalent after the whole line has been evaluated
Das stimmt aber nicht unbedingt. Was ist, wenna
es sich um eine Methode handelt? Methoden können Nebenwirkungen haben. ZB mitpublic; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
,self.a ||= b
wird 6 zurückgeben,self.a ? self.a : self.a = b
wird aber 7 zurückgeben.Kurz gesagt,
a||=b
bedeutet: Wenna
istundefined, nil or false
, assignb
zua
. Ansonstena
intakt bleiben .quelle
x ||= y
meintWenn
x
ein Wert vorhanden ist, lassen Sie ihn in Ruhe und ändern Sie den Wert nicht, andernfalls setzen Sie ihnx
aufy
quelle
Es bedeutet oder gleich. Es wird geprüft, ob der Wert auf der linken Seite definiert ist, und dieser dann verwendet. Wenn nicht, verwenden Sie den Wert rechts. Sie können es in Rails verwenden, um Instanzvariablen in Modellen zwischenzuspeichern.
Ein kurzes Rails-basiertes Beispiel, in dem wir eine Funktion zum Abrufen des aktuell angemeldeten Benutzers erstellen:
Es wird überprüft, ob die Instanzvariable @current_user festgelegt ist. Wenn dies der Fall ist, wird es zurückgegeben, wodurch ein Datenbankaufruf gespeichert wird. Wenn es jedoch nicht gesetzt ist, rufen wir auf und setzen dann die Variable @current_user darauf. Es ist eine wirklich einfache Caching-Technik, eignet sich jedoch hervorragend, wenn Sie dieselbe Instanzvariable mehrmals in der Anwendung abrufen.
quelle
undefined
, sondern auch auffalse
undnil
, was möglicherweise nicht relevant istcurrent_user
, aber insbesonderefalse
in anderen Fällen unerwartet sein kannist
"Wenn x falsch oder undefiniert ist, zeigt x auf y"
quelle
Um genau zu sein,
a ||= b
bedeutet "wenna
undefiniert oder falsch (false
odernil
), setzena
aufb
und bewerten auf (dh zurück)b
, andernfalls bewerten aufa
".Andere versuchen oft, dies zu veranschaulichen, indem sie sagen, dass dies
a ||= b
gleichbedeutend mita || a = b
oder ista = a || b
. Diese Äquivalenzen können hilfreich sein, um das Konzept zu verstehen. Beachten Sie jedoch, dass sie nicht unter allen Bedingungen korrekt sind. Lassen Sie mich erklären:a ||= b
⇔a || a = b
?Das Verhalten dieser Anweisungen unterscheidet sich, wenn
a
es sich um eine undefinierte lokale Variable handelt. In diesem Falla ||= b
wird festgelegta
aufb
(und zu bewerten , umb
), währenda || a = b
erhöhen wirdNameError: undefined local variable or method 'a' for main:Object
.a ||= b
⇔a = a || b
?Die Gleichwertigkeit dieser Aussagen werden häufig angenommen, da eine ähnliche Gleichwertigkeit gilt für andere abgekürzte Zuordnung Operatoren (dh
+=
,-=
,*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
, und>>=
). Das||=
Verhalten dieser Aussagen kann sich jedoch unterscheiden, wenna=
es sich um eine Methode für ein Objekt handelt und diesea
wahr ist. In diesem Falla ||= b
wird nichts tun (außer bewerten zua
), währenda = a || b
rufta=(a)
aufa
‚s Empfänger. Wie andere bereits betont haben, kann dies einen Unterschied machen, wenn das Aufrufena=a
Nebenwirkungen hat, z. B. das Hinzufügen von Schlüsseln zu einem Hash.a ||= b
⇔a = b unless a
??Das Verhalten dieser Aussagen unterscheidet sich nur darin, was sie bewerten, wenn
a
es wahr ist. In diesem Falla = b unless a
wird ausgewertetnil
(obwohla
immer noch nicht wie erwartet festgelegt), währenda ||= b
ausgewertet wirda
.a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????Immer noch nein. Diese Anweisungen können unterschiedlich sein, wenn eine
method_missing
Methode vorhanden ist, die einen wahrheitsgemäßen Wert für zurückgibta
. In diesem Falla ||= b
wird unabhängig bewertenmethod_missing
zurückgibt, und versuchen , nicht zu Satza
, währenddefined?(a) ? (a || a = b) : (a = b)
gesetzta
zub
und bewerten zub
.Okay, okay, was ist also
a ||= b
gleichbedeutend mit? Gibt es eine Möglichkeit, dies in Ruby auszudrücken?Unter der Annahme, dass ich nichts übersehen habe, glaube ich, dass dies
a ||= b
funktional gleichbedeutend ist mit ... ( Trommelwirbel )Warten Sie mal! Ist das nicht nur das erste Beispiel mit einem Noop davor? Nicht ganz. Erinnern Sie sich, wie ich vorher sagte, dass dies
a ||= b
nur nicht gleichbedeutend ist mita || a = b
wanna
ist eine undefinierte lokale Variable? Nun,a = nil if false
stellt sicher, dass diesa
niemals undefiniert ist, obwohl diese Zeile niemals ausgeführt wird. Lokale Variablen in Ruby haben einen lexikalischen Gültigkeitsbereich.quelle
(a=b unless a) or a
a
es sich um eine Methode handelt, wird sie zweimal statt einmal aufgerufen (wenn sie beim ersten Mal einen wahrheitsgemäßen Wert zurückgibt). Dies kann zu unterschiedlichen Verhaltensweisen führen, wenn beispielsweisea
die Rückkehr lange dauert oder Nebenwirkungen auftreten.b
zua
, nicht die rhs noch auf die linke Skala zuweisen, oder in anderen Worten, nicht die linke Skala immer noch seinen Wert auf den rhs eingestellt?a ||= b
Antwort, die ich im Internet gefunden habe. Vielen Dank.unless x x = y end
Wenn x keinen Wert hat (es ist nicht null oder falsch), setzen Sie ihn gleich y
ist äquivalent zu
x ||= y
quelle
Angenommen,
a = 2
undb = 3
DANN,
a ||= b
wird in Folge zua
‚s - Wert , dh2
.Als ob eine Bewertung zu einem Wert führt, der nicht zu
false
oder führtnil
. Deshalb wird der Wertll
nicht bewertetb
.Angenommen,
a = nil
undb = 3
.Dann ergibt sich der Wert
a ||= b
von3
ieb
.Beim ersten Versuch, den Wert von a zu bewerten, der zu ... führte, wurde der Wert von a
nil
ausgewertetb
.Das beste Beispiel in der ror App ist:
Wo,
User.find_by_id(session[:user_id])
wird genau dann ausgelöst, wenn es@current_user
vorher nicht initialisiert wurde.quelle
a || = b
Gibt an, ob in 'a' ein Wert vorhanden ist und Sie ihn nicht ändern möchten, wenn dieser Wert weiterhin verwendet wird. Wenn 'a' keinen Wert hat, verwenden Sie den Wert 'b'.
Einfache Wörter, wenn die linke Seite nicht null ist, zeigen auf den vorhandenen Wert, andernfalls auf den Wert auf der rechten Seite.
quelle
ist äquivalent zu
und nicht
aufgrund der Situation, in der Sie einen Hash mit einem Standard definieren (der Hash gibt den Standard für alle nicht definierten Schlüssel zurück)
wenn du benutzt:
a ist immer noch:
aber wenn du es so schreibst:
a wird:
Da Sie den Wert von sich selbst bei key zugewiesen haben
10
, der standardmäßig true ist, wird jetzt der Hash für den Schlüssel definiert10
, anstatt die Zuweisung überhaupt nicht auszuführen.quelle
Es ist wie eine faule Instanziierung. Wenn die Variable bereits definiert ist, wird dieser Wert verwendet, anstatt den Wert erneut zu erstellen.
quelle
Bitte denken Sie auch daran, dass dies
||=
keine atomare Operation ist und daher nicht threadsicher. Verwenden Sie es als Faustregel nicht für Klassenmethoden.quelle
Dies ist die Standardzuweisungsnotation
Beispiel: x || = 1
Hiermit wird überprüft, ob x null ist oder nicht. Wenn x tatsächlich Null ist, wird ihm dieser neue Wert zugewiesen (1 in unserem Beispiel).
expliziter:
wenn x == nil
x = 1
end
quelle
nil
oderfalse
nicht nurnil
|| = ist ein bedingter Zuweisungsoperator
ist äquivalent zu
oder alternativ
quelle
Wenn
X
KEIN Wert vorhanden ist, wird ihm der Wert von zugewiesenY
. Andernfalls wird der ursprüngliche Wert 5 in diesem Beispiel beibehalten:quelle
Als weit verbreitetes Missverständnis
a ||= b
ist es nicht gleichbedeutend mita = a || b
, aber es verhält sich wiea || a = b
.Aber hier kommt ein kniffliger Fall. Wenn
a
nicht definiert, wirda || a = 42
ausgelöstNameError
, währenda ||= 42
zurückgegeben wird42
. Sie scheinen also keine äquivalenten Ausdrücke zu sein.quelle
||=
weist rechts nur dann einen Wert zu, wenn left == nil ist (oder undefiniert oder false ist).quelle
Diese Ruby-Lang-Syntax. Die richtige Antwort ist, die Ruby-Lang-Dokumentation zu überprüfen. Alle anderen Erklärungen sind verschleiert .
Google
"ruby-lang docs Abkürzung".
Ruby-lang-Dokumente
https://docs.ruby-lang.org/de/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
quelle
Weil
a
schon eingestellt war1
Weil
a
warnil
quelle
Dies bedeutet:
was sein wird
so endlich
Wenn Sie dies jetzt noch einmal anrufen:
Wenn Sie dies jetzt noch einmal anrufen:
Wenn Sie dies beobachten, wird der
b
Wert nicht zugewiesena
.a
wird noch haben5
.Es ist ein Memoization Pattern, das in Ruby verwendet wird, um Accessoren zu beschleunigen.
Dies bedeutet im Grunde:
Sie rufen also zum ersten Mal die Datenbank auf, wenn Sie diese Methode aufrufen.
Zukünftige Aufrufe dieser Methode geben nur den Wert der
@users
Instanzvariablen zurück.quelle
||=
wird als bedingter Zuweisungsoperator bezeichnet.Es funktioniert grundsätzlich so,
=
aber mit der Ausnahme, dass eine Variable, die bereits zugewiesen wurde , nichts tut.Erstes Beispiel:
Zweites Beispiel:
Im ersten Beispiel
x
ist jetzt gleich 10. Im zweiten Beispielx
ist jedoch bereits als 20 definiert. Der bedingte Operator hat also keine Auswirkung.x
ist nach dem Laufen noch 20x ||= 10
.quelle
a ||= b
ist das gleiche wie sagena = b if a.nil?
odera = b unless a
Aber zeigen alle 3 Optionen die gleiche Leistung? Mit Ruby 2.5.1 dies
dauert auf meinem PC 0,099 Sekunden
dauert 0,062 Sekunden. Das ist fast 40% schneller.
und dann haben wir auch:
Das dauert 0,166 Sekunden.
Nicht, dass dies im Allgemeinen erhebliche Auswirkungen auf die Leistung haben würde. Wenn Sie jedoch die letzte Optimierung benötigen, sollten Sie dieses Ergebnis berücksichtigen. Übrigens:
a = 1 unless a
ist für Anfänger leichter zu lesen, selbsterklärend.Hinweis 1: Der Grund für die mehrfache Wiederholung der Zuweisungszeile besteht darin, den Overhead der Schleife für die gemessene Zeit zu verringern.
Anmerkung 2: Die Ergebnisse sind ähnlich, wenn ich
a=nil
vor jeder Aufgabe nichts mache .quelle