Was bedeutet der folgende Code in Ruby?
||=
Hat es eine Bedeutung oder einen Grund für die Syntax?
Was bedeutet der folgende Code in Ruby?
||=
Hat es eine Bedeutung oder einen Grund für die Syntax?
Antworten:
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, wenn a
undefiniert oder falsch ist , dann bewerten b
und a
auf das Ergebnis setzen . Entsprechend wird, wenn a
es definiert ist und als wahr bewertet wird, b
es nicht bewertet und es findet keine Zuordnung statt. Zum Beispiel:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Verwirrenderweise sieht es anderen Zuweisungsoperatoren (z. B. +=
) ähnlich , verhält sich jedoch anders.
a += b
wird übersetzt in a = a + b
a ||= b
grob übersetzt zu a || a = b
Es ist eine Abkürzung für a || a = b
. Der Unterschied ist, dass, wenn a
undefiniert ist, a || a = b
erhöhen würde NameError
, während a ||= b
setzt a
auf b
. Diese Unterscheidung ist unwichtig , ob a
und b
beiden lokalen Variablen sind, ist jedoch von Bedeutung , wenn entweder ein Getter / Setter - Methode einer Klasse ist.
Weiterführende Literatur:
h = Hash.new(0); h[1] ||= 2
. Betrachten wir nun die beiden möglichen Erweiterungen h[1] = h[1] || 2
vs h[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 Antwort
a || a = b
wirft ein NameError
Wenn a
ist undefiniert. a ||= b
nicht, sondern initialisiert a
und setzt es auf b
. Das ist meines Wissens der einzige Unterschied zwischen den beiden. Ebenso ist der einzige Unterschied zwischen a = a || b
und a ||= b
mir bewusst, dass a=
eine Methode aufgerufen wird, unabhängig davon, was a
zurückgegeben wird. Der einzige Unterschied zwischen a = b unless a
und a ||= b
mir ist bewusst, dass diese Aussage bewertet wird, nil
anstatt a
ob sie a
wahr ist. Viele Annäherungen, aber nichts ganz Äquivalentes ...
a ||= b
wertet genauso aus wie jede der folgenden Zeilen
a || a = b
a ? a : a = b
if a then a else a = b end
- -
Auf der anderen Seite,
a = a || b
wertet genauso aus wie jede der folgenden Zeilen
a = a ? a : b
if a then a = a else a = b end
- -
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.
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
, Und if a then a = a else a = b end
wirft einen Fehler , wenn a
nicht definiert ist, während a ||= b
und a = a || b
wird nicht. Auch a || a = b
, a ? a : a = b
, if a then a else a = b end
, a = a ? a : b
, und if a then a = a else a = b end
bewerten a
zweimal , wenn a
truthy ist, während a ||= b
und a = a || b
dies nicht tun.
a || a = b
Wird nicht a
zweimal ausgewertet, wenn dies a
zutrifft.
the end state will be equivalent after the whole line has been evaluated
Das stimmt aber nicht unbedingt. Was ist, wenn a
es sich um eine Methode handelt? Methoden können Nebenwirkungen haben. ZB mit public; 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.
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:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
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.
undefined
, sondern auch auf false
und nil
, was möglicherweise nicht relevant ist current_user
, aber insbesondere false
in anderen Fällen unerwartet sein kann
x ||= y
ist
x || x = y
"Wenn x falsch oder undefiniert ist, zeigt x auf y"
Um genau zu sein, a ||= b
bedeutet "wenn a
undefiniert oder falsch ( false
oder nil
), setzen a
auf b
und bewerten auf (dh zurück) b
, andernfalls bewerten auf a
".
Andere versuchen oft, dies zu veranschaulichen, indem sie sagen, dass dies a ||= b
gleichbedeutend mit a || a = b
oder ist a = 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 Fall a ||= b
wird festgelegt a
auf b
(und zu bewerten , um b
), während a || a = b
erhöhen wird NameError: 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, wenn a=
es sich um eine Methode für ein Objekt handelt und diese a
wahr ist. In diesem Fall a ||= b
wird nichts tun (außer bewerten zu a
), während a = a || b
ruft a=(a)
auf a
‚s Empfänger. Wie andere bereits betont haben, kann dies einen Unterschied machen, wenn das Aufrufen a=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 Fall a = b unless a
wird ausgewertet nil
(obwohl a
immer noch nicht wie erwartet festgelegt), während a ||= b
ausgewertet wird a
.
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ückgibt a
. In diesem Fall a ||= b
wird unabhängig bewerten method_missing
zurückgibt, und versuchen , nicht zu Satz a
, während defined?(a) ? (a || a = b) : (a = b)
gesetzt a
zu b
und bewerten zu b
.
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 )
begin
a = nil if false
a || a = b
end
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 mit a || a = b
wann a
ist eine undefinierte lokale Variable? Nun, a = nil if false
stellt sicher, dass dies a
niemals undefiniert ist, obwohl diese Zeile niemals ausgeführt wird. Lokale Variablen in Ruby haben einen lexikalischen Gültigkeitsbereich.
(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 beispielsweise a
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.
Angenommen, a = 2
undb = 3
DANN, a ||= b
wird in Folge zu a
‚s - Wert , dh 2
.
Als ob eine Bewertung zu einem Wert führt, der nicht zu false
oder führt nil
. Deshalb wird der Wert ll
nicht bewertet b
.
Angenommen, a = nil
und b = 3
.
Dann ergibt sich der Wert a ||= b
von 3
ie b
.
Beim ersten Versuch, den Wert von a zu bewerten, der zu ... führte, wurde der Wert von a nil
ausgewertet b
.
Das beste Beispiel in der ror App ist:
#To get currently logged in iser
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
Wo, User.find_by_id(session[:user_id])
wird genau dann ausgelöst, wenn es @current_user
vorher nicht initialisiert wurde.
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.
a ||= b
ist äquivalent zu
a || a = b
und nicht
a = a || b
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)
a = Hash.new(true) #Which is: {}
wenn du benutzt:
a[10] ||= 10 #same as a[10] || a[10] = 10
a ist immer noch:
{}
aber wenn du es so schreibst:
a[10] = a[10] || 10
a wird:
{10 => true}
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 definiert 10
, anstatt die Zuweisung überhaupt nicht auszuführen.
Bitte denken Sie auch daran, dass dies ||=
keine atomare Operation ist und daher nicht threadsicher. Verwenden Sie es als Faustregel nicht für Klassenmethoden.
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
nil
oder false
nicht nurnil
Wenn X
KEIN Wert vorhanden ist, wird ihm der Wert von zugewiesen Y
. Andernfalls wird der ursprüngliche Wert 5 in diesem Beispiel beibehalten:
irb(main):020:0> x = 5
=> 5
irb(main):021:0> y = 10
=> 10
irb(main):022:0> x ||= y
=> 5
# Now set x to nil.
irb(main):025:0> x = nil
=> nil
irb(main):026:0> x ||= y
=> 10
Als weit verbreitetes Missverständnis a ||= b
ist es nicht gleichbedeutend mit a = a || b
, aber es verhält sich wie a || a = b
.
Aber hier kommt ein kniffliger Fall. Wenn a
nicht definiert, wird a || a = 42
ausgelöst NameError
, während a ||= 42
zurückgegeben wird 42
. Sie scheinen also keine äquivalenten Ausdrücke zu sein.
||=
weist rechts nur dann einen Wert zu, wenn left == nil ist (oder undefiniert oder false ist).
Diese Ruby-Lang-Syntax. Die richtige Antwort ist, die Ruby-Lang-Dokumentation zu überprüfen. Alle anderen Erklärungen sind verschleiert .
"ruby-lang docs Abkürzung".
https://docs.ruby-lang.org/de/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
Dies bedeutet:
a = a || b
was sein wird
a = nil || 5
so endlich
a = 5
Wenn Sie dies jetzt noch einmal anrufen:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Wenn Sie dies jetzt noch einmal anrufen:
a ||= b
a = a || b
a = 5 || 6
a = 5
Wenn Sie dies beobachten, wird der b
Wert nicht zugewiesen a
. a
wird noch haben 5
.
Es ist ein Memoization Pattern, das in Ruby verwendet wird, um Accessoren zu beschleunigen.
def users
@users ||= User.all
end
Dies bedeutet im Grunde:
@users = @users || User.all
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.
||=
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:
x ||= 10
Zweites Beispiel:
x = 20
x ||= 10
Im ersten Beispiel x
ist jetzt gleich 10. Im zweiten Beispiel x
ist jedoch bereits als 20 definiert. Der bedingte Operator hat also keine Auswirkung. x
ist nach dem Laufen noch 20 x ||= 10
.
a ||= b
ist das gleiche wie sagen a = b if a.nil?
odera = b unless a
Aber zeigen alle 3 Optionen die gleiche Leistung? Mit Ruby 2.5.1 dies
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
dauert auf meinem PC 0,099 Sekunden
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
dauert 0,062 Sekunden. Das ist fast 40% schneller.
und dann haben wir auch:
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
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 .