Was bedeutet || = (oder gleich) in Ruby?

340

Was bedeutet der folgende Code in Ruby?

||=

Hat es eine Bedeutung oder einen Grund für die Syntax?

Collimarco
quelle

Antworten:

175

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.

Jörg W Mittag
quelle
2
Der zweite Link hat unter Bit Rot gelitten (Kommentar von Meta von stackoverflow.com/users/540162/nightfirecat ).
Andrew Grimm
330
Das ist eine sehr kryptische Nichtantwort. Die kurze Antwort scheint zu sein: a || = b bedeutet, wenn a undefiniert ist, weisen Sie ihm den Wert von b zu, andernfalls lassen Sie es in Ruhe. (Ok, es gibt Nuancen und Sonderfälle, aber das ist der Grundfall.)
Steve Bennett
20
@SteveBennett: Ich würde die Tatsache, a = false; a ||= truedie nicht das tut, was Ihre Antwort sagt, nicht als "Nuance" bezeichnen.
Jörg W Mittag
23
Vielleicht wurde diese Frage so oft gestellt, weil die Leute immer wieder antworten, dass diese Frage so oft gestellt wurde.
Unschuldig
8
Mit dieser Antwort ist leicht zu erkennen, warum es mehrere Threads gibt. Wenn Sie versuchen, mit einem Anfängerhut nach einer Antwort auf diese Frage zu suchen, werden Sie feststellen, dass nicht alle Antworten klar sind. Zum Beispiel sagen Sie mit diesem nur, was nicht ist. Ich schlage vor, Ihre Antwort zu verbessern und Anfängern eine einfache Antwort zu geben: a = b, es sei denn, a
Arnold Roa
594

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 + b
  • a ||= b grob übersetzt zu a || a = b

Es 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:

Steve Bennett
quelle
52
Vielen Dank für diese Antwort, es macht viel mehr Sinn.
Tom Hert
Ich habe nicht genug gesucht, verstehe aber immer noch nicht, warum Sie dies im Gegensatz zu a = a || verwenden würden b. vielleicht nur meine persönliche Meinung, aber ein wenig lächerlich, dass eine solche Nuance existiert ...
dtc
2
@dtc, überlegen 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
verwiesen wird
1
Ich mag die andere Antwort, weil sie so ausführlich ist, aber ich liebe diese Antwort wegen ihrer Einfachheit. Für jemanden, der Ruby lernt, ist dies die Art von Antwort, die wir brauchen. Wenn wir wüssten, was || = bedeutet, wäre die Frage wahrscheinlich anders formuliert worden.
OBCENEIKON
1
Zu Ihrer Information, 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 ...
Ajedi32
32

Prägnante und vollständige Antwort

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.

the_minted
quelle
1
bist du sicher? Dies bedeutet, dass wenn afalse / zero / undefined ist, es zweimal ausgewertet wird. (Aber ich kenne Ruby nicht, also weiß ich nicht, ob lWerte genau 'ausgewertet' werden können ...)
Steve Bennett
Ich verstehe, was du sagst. Was ich damit gemeint habe, dass zwei Zeilen äquivalent sind, ist, dass der Endzustand äquivalent ist, nachdem die gesamte Zeile ausgewertet wurde, dh den Wert von a, b und was zurückgegeben wird. Es ist durchaus möglich, ob Ruby-Dolmetscher unterschiedliche Zustände verwenden - wie mehrere Bewertungen von a -, um dorthin zu gelangen. Gibt es Experten für Ruby-Dolmetscher?
the_minted
3
Das ist nicht ganz richtig. 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.
Ajedi32
1
* Korrektur: a || a = bWird nicht azweimal ausgewertet, wenn dies azutrifft.
Ajedi32
1
@the_minted 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.
Ajedi32
27

Kurz gesagt, a||=bbedeutet: Wenn aist undefined, nil or false, assign bzu a. Ansonsten aintakt bleiben .

Vidang
quelle
16
Grundsätzlich,


x ||= y meint

Wenn xein Wert vorhanden ist, lassen Sie ihn in Ruhe und ändern Sie den Wert nicht, andernfalls setzen Sie ihn xaufy

nPcomp
quelle
13

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.

Jamie Rumbelow
quelle
8
Das ist falsch. Bitte lesen Sie Ruby-Forum.Com/topic/151660 und die darin enthaltenen Links.
Jörg W Mittag
1
@ Jo (umlaut) rg, ich sehe nicht, was daran falsch ist. Ihr Link ist eine Liste anderer Links. Keine wirkliche Erklärung, warum es falsch ist, klingt nur nach einem Werturteil über Ihr Ende.
Eggmatters
Diese Antwort ist falsch, weil sie nicht nur ausgelöst wird undefined, sondern auch auf falseund nil, was möglicherweise nicht relevant ist current_user, aber insbesondere falsein anderen Fällen unerwartet sein kann
dfherr
Trotz aller Unvollständigkeiten, die diese Antwort möglicherweise aufweist (funktioniert nicht für nil / false), erklärt sie als erste, warum Sie || = verwenden möchten. Vielen Dank!
Jonathan Tuzman
8
x ||= y

ist

x || x = y

"Wenn x falsch oder undefiniert ist, zeigt x auf y"

Kiattisak Anoochitarom
quelle
8

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 ||= ba || 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 ||= ba = 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 ||= ba = 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 ||= bdefined?(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.

Ajedi32
quelle
Also Ihr erweitertes drittes Beispiel:(a=b unless a) or a
Vol7ron
1
@ vol7ron Das hat ein ähnliches Problem wie # 2. Wenn 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.
Ajedi32
Auch Satz sollte es nicht sagen assign 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?
Vol7ron
Beste a ||= bAntwort, die ich im Internet gefunden habe. Vielen Dank.
Eric Duminil
3

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

0r4cl3
quelle
3

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.

Pankhuri
quelle
3

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.

SHUBHAM SHARMA
quelle
2
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.

RileyE
quelle
2

Es ist wie eine faule Instanziierung. Wenn die Variable bereits definiert ist, wird dieser Wert verwendet, anstatt den Wert erneut zu erstellen.

mukh007
quelle
2

Bitte denken Sie auch daran, dass dies ||=keine atomare Operation ist und daher nicht threadsicher. Verwenden Sie es als Faustregel nicht für Klassenmethoden.

Luca Guidi
quelle
2

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

Max Rogers
quelle
entweder niloder falsenicht nurnil
Alex Poca
2

|| = ist ein bedingter Zuweisungsoperator

  x ||= y

ist äquivalent zu

  x = x || y

oder alternativ

if defined?(x) and x
    x = x
else 
    x = y
end
Sunda
quelle
2

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
zee
quelle
1

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.

Tessie
quelle
1

||= weist rechts nur dann einen Wert zu, wenn left == nil ist (oder undefiniert oder false ist).

H. Almidan
quelle
Sie meinten wahrscheinlich "weist links einen Wert zu" statt rechts
Maysam Torabi
0
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1

Weil aschon eingestellt war1

irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2

Weil awarnil

Fulvio
quelle
Was ist das beantwortete Datum hier. Warum zeigt es kein Jahr?
Shiv
0
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.

Siva Krishna Reddy
quelle
0

||= 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.

Charlie Wood
quelle
-2

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 .

Ymox
quelle