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 adefiniert 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 ||= truedie nicht das tut, was Ihre Antwort sagt, nicht als "Nuance" bezeichnen.
a ||= bist ein bedingter Zuweisungsoperator . Es bedeutet, wenn aundefiniert oder falsch ist , dann bewerten bund aauf das Ergebnis setzen . Entsprechend wird, wenn aes definiert ist und als wahr bewertet wird, bes 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 + ba ||= b grob übersetzt zu a || a = bEs ist eine Abkürzung für a || a = b. Der Unterschied ist, dass, wenn aundefiniert ist, a || a = berhöhen würde NameError, während a ||= bsetzt aauf b. Diese Unterscheidung ist unwichtig , ob aund bbeiden 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] || 2vs h[1] || h[1] = 2. Beide Ausdrücke werden ausgewertet, 0aber 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 = bwirft ein NameErrorWenn aist undefiniert. a ||= bnicht, sondern initialisiert aund setzt es auf b. Das ist meines Wissens der einzige Unterschied zwischen den beiden. Ebenso ist der einzige Unterschied zwischen a = a || bund a ||= bmir bewusst, dass a=eine Methode aufgerufen wird, unabhängig davon, was azurückgegeben wird. Der einzige Unterschied zwischen a = b unless aund a ||= bmir ist bewusst, dass diese Aussage bewertet wird, nilanstatt aob sie awahr 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.
afalse / 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 endwirft einen Fehler , wenn anicht definiert ist, während a ||= bund a = a || bwird 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 endbewerten azweimal , wenn atruthy ist, während a ||= bund a = a || bdies nicht tun.
a || a = bWird nicht azweimal ausgewertet, wenn dies azutrifft.
the end state will be equivalent after the whole line has been evaluatedDas stimmt aber nicht unbedingt. Was ist, wenn aes 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 ||= bwird 6 zurückgeben, self.a ? self.a : self.a = bwird 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 falseund nil, was möglicherweise nicht relevant ist current_user, aber insbesondere falsein 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 ||= bbedeutet "wenn aundefiniert oder falsch ( falseoder nil), setzen aauf bund bewerten auf (dh zurück) b, andernfalls bewerten auf a".
Andere versuchen oft, dies zu veranschaulichen, indem sie sagen, dass dies a ||= bgleichbedeutend mit a || a = boder 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 aes sich um eine undefinierte lokale Variable handelt. In diesem Fall a ||= bwird festgelegt aauf b(und zu bewerten , um b), während a || a = berhö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 awahr ist. In diesem Fall a ||= bwird nichts tun (außer bewerten zu a), während a = a || bruft a=(a)auf a‚s Empfänger. Wie andere bereits betont haben, kann dies einen Unterschied machen, wenn das Aufrufen a=aNebenwirkungen 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 aes wahr ist. In diesem Fall a = b unless awird ausgewertet nil(obwohl aimmer noch nicht wie erwartet festgelegt), während a ||= bausgewertet wird a.
a ||= b⇔defined?(a) ? (a || a = b) : (a = b) ????
Immer noch nein. Diese Anweisungen können unterschiedlich sein, wenn eine method_missingMethode vorhanden ist, die einen wahrheitsgemäßen Wert für zurückgibt a. In diesem Fall a ||= bwird unabhängig bewerten method_missingzurückgibt, und versuchen , nicht zu Satz a, während defined?(a) ? (a || a = b) : (a = b)gesetzt azu bund 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 ||= bfunktional 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 ||= bnur nicht gleichbedeutend ist mit a || a = bwann aist eine undefinierte lokale Variable? Nun, a = nil if falsestellt sicher, dass dies aniemals undefiniert ist, obwohl diese Zeile niemals ausgeführt wird. Lokale Variablen in Ruby haben einen lexikalischen Gültigkeitsbereich.
(a=b unless a) or a
aes 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 adie Rückkehr lange dauert oder Nebenwirkungen auftreten.
bzua , 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 ||= bAntwort, die ich im Internet gefunden habe. Vielen Dank.
Angenommen, a = 2undb = 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 falseoder führt nil. Deshalb wird der Wert llnicht bewertet b.
Angenommen, a = nilund b = 3.
Dann ergibt sich der Wert a ||= bvon 3ie b.
Beim ersten Versuch, den Wert von a zu bewerten, der zu ... führte, wurde der Wert von a nilausgewertet 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_uservorher 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
niloder falsenicht nurnil
Wenn XKEIN 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 ||= bist es nicht gleichbedeutend mit a = a || b, aber es verhält sich wie a || a = b.
Aber hier kommt ein kniffliger Fall. Wenn anicht definiert, wird a || a = 42ausgelöst NameError, während a ||= 42zurü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 bWert nicht zugewiesen a. awird 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 @usersInstanzvariablen 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 xist jetzt gleich 10. Im zweiten Beispiel xist jedoch bereits als 20 definiert. Der bedingte Operator hat also keine Auswirkung. xist nach dem Laufen noch 20 x ||= 10.
a ||= bist 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 aist 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=nilvor jeder Aufgabe nichts mache .