Co oznacza następujący kod w Ruby?
||=
Czy ma to jakieś znaczenie lub przyczynę dla składni?
To pytanie było omawiane tak często na listach mailingowych Ruby i blogach Ruby, że teraz są nawet wątki na liście mailingowej Ruby, których jedynym celem jest zebranie linków do wszystkich innych wątków na liście mailingowej Ruby, które omawiają ten problem .
Oto jeden: Ostateczna lista wątków i stron || = (OR Equal)
Jeśli naprawdę chcesz wiedzieć, co się dzieje, zapoznaj się z sekcją 11.4.2.3 „Skrócone zadania” specyfikacji wersji językowej Ruby .
Jako pierwsze przybliżenie
a ||= b
jest równa
a || a = b
i nie równoważne z
a = a || b
Jest to jednak tylko pierwsze przybliżenie, zwłaszcza jeśli a
jest niezdefiniowane. Semantyka różni się również w zależności od tego, czy jest to proste przypisanie zmiennej, przypisanie metody czy przypisanie indeksowania:
a ||= b
a.c ||= b
a[c] ||= b
wszyscy są traktowani inaczej.
a = false; a ||= true
ma nie robić to, co twoja odpowiedź mówi, że robi „niuans”.a ||= b
jest operatorem przypisania warunkowego . Oznacza to, że jeślia
jest niezdefiniowany lub falsey , to oceńb
i ustawa
wynik . Odpowiednio, jeślia
jest zdefiniowane i ocenia zgodnie z prawdą,b
to nie jest oceniane i nie ma miejsca przypisanie. Na przykład:Myląco wygląda podobnie do innych operatorów przypisania (takich jak
+=
), ale zachowuje się inaczej.a += b
przetłumaczyć naa = a + b
a ||= b
z grubsza przekłada się naa || a = b
Jest to prawie skrót dla
a || a = b
. Różnica polega na tym, że gdya
jest niezdefiniowany,a || a = b
podnosi sięNameError
, aa ||= b
ustawiaa
nab
. To rozróżnienie nie jest ważne, jeślia
ib
oba są zmiennymi lokalnymi, ale jest znaczące, jeśli jest metodą getter / setter klasy.Dalsza lektura:
źródło
h = Hash.new(0); h[1] ||= 2
. Rozważmy teraz dwa możliwe rozszerzeniah[1] = h[1] || 2
vsh[1] || h[1] = 2
. Oba wyrażenia oceniają na,0
ale pierwsze niepotrzebnie zwiększa rozmiar skrótu. Być może dlatego Matz postanowił sprawić, by||=
zachowywał się bardziej jak drugie rozszerzenie. (a || a = b
podnosiNameError
ifa
jest niezdefiniowane.a ||= b
nie, ale zamiast tego inicjujea
i ustawia nab
. To jedyne rozróżnienie między tymi dwoma, o ile mi wiadomo. Podobnie, jedyną różnicą międzya = a || b
ia ||= b
, o której jestem świadomy, jest to, że jeślia=
metoda jest wywoływana, niezależnie od tego, coa
zwróci. Poza tym jedyną różnicą między mną,a = b unless a
aa ||= b
tym, o czym jestem świadomy, jest to, że to stwierdzenie sprawdza, czynil
zamiast jest prawdziwe. Wiele przybliżeń, ale nic zupełnie równoważnego ...a
a
Zwięzła i pełna odpowiedź
ocenia w taki sam sposób, jak każdy z poniższych wierszy
-
Z drugiej strony,
ocenia w taki sam sposób, jak każdy z poniższych wierszy
-
Edycja: Jak zauważył AJedi32 w komentarzach, jest to prawdą tylko wtedy, gdy: 1. a jest zmienną zdefiniowaną. 2. Ocena raz i dwa razy nie powoduje różnicy w stanie programu lub systemu.
źródło
a
false = zero / undefined, to jest oceniane dwukrotnie. (Ale nie znam Ruby, więc nie wiem, czy wartości można dokładnie „ocenić”)a || a = b
,a ? a : a = b
,if a then a else a = b end
, Iif a then a = a else a = b end
wygeneruje błąd, jeślia
jest niezdefiniowana, natomiasta ||= b
ia = a || b
nie będzie. Ponadtoa || a = b
,a ? a : a = b
,if a then a else a = b end
,a = a ? a : b
, iif a then a = a else a = b end
ocenića
dwukrotnie gdya
jest truthy, natomiasta ||= b
ia = a || b
nie.a || a = b
nie będzie oceniaća
dwukrotnie, kiedya
jest to prawda.the end state will be equivalent after the whole line has been evaluated
To niekoniecznie prawda. Co jeślia
jest metodą? Metody mogą mieć skutki uboczne. Nppublic; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
,self.a ||= b
powróci 6, aleself.a ? self.a : self.a = b
powróci 7.W skrócie
a||=b
oznacza: Jeślia
takundefined, nil or false
, przypiszb
doa
. W przeciwnym razie zachowaja
nienaruszony.źródło
x ||= y
znaczyjeśli
x
ma jakąkolwiek wartość, zostaw ją w spokoju i nie zmieniaj wartości, w przeciwnym razie ustawx
nay
źródło
Oznacza or-równa się. Sprawdza, czy wartość po lewej jest zdefiniowana, a następnie użyj jej. Jeśli nie, użyj wartości po prawej stronie. Możesz go użyć w Railsach do buforowania zmiennych instancji w modelach.
Szybki przykład oparty na Railsach, w którym tworzymy funkcję pobierania aktualnie zalogowanego użytkownika:
Sprawdza, czy ustawiona jest zmienna instancji @current_user. Jeśli tak, zwróci go, zapisując w ten sposób wywołanie bazy danych. Jeśli jednak nie jest ustawiony, wykonujemy wywołanie, a następnie ustawiamy na to zmienną @current_user. Jest to bardzo prosta technika buforowania, ale doskonale nadaje się do wielokrotnego pobierania tej samej zmiennej instancji w aplikacji.
źródło
undefined
, ale także włączafalse
inil
, co może nie mieć znaczeniacurrent_user
, ale szczególniefalse
może być nieoczekiwana w innych przypadkachjest
„jeśli x jest fałszywe lub niezdefiniowane, to x wskazuje na y”
źródło
Mówiąc dokładniej,
a ||= b
oznacza „jeślia
jest niezdefiniowany lub fałszywy (false
lubnil
), ustawa
nab
i oceń na (tj. Zwróć )b
, w przeciwnym razie oceń naa
”.Inni często próbują to zilustrować, mówiąc, że
a ||= b
jest to równoważne za || a = b
luba = a || b
. Te równoważności mogą być pomocne w zrozumieniu pojęcia, ale należy pamiętać, że nie są one dokładne we wszystkich warunkach. Pozwól mi wyjaśnić:a ||= b
⇔a || a = b
?Zachowanie tych instrukcji różni się, gdy
a
jest niezdefiniowaną zmienną lokalną. W takim przypadkua ||= b
ustawi sięa
nab
(i ocenia nab
), podczas gdya || a = b
podniesieNameError: undefined local variable or method 'a' for main:Object
.a ||= b
⇔a = a || b
?Równoważność z tych stwierdzeń są często zakłada się, gdyż podobna równoważność jest prawdziwe dla innych skrótem przypisania operatorów (czyli
+=
,-=
,*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
, i>>=
). Jednak||=
zachowanie tych instrukcji może się różnić, gdya=
jest metodą na obiekcie ia
jest prawdziwa. W takim przypadkua ||= b
nie zrobi nic (prócz ocenya
), aa = a || b
zadzwonia=(a)
doa
odbiornika. Jak zauważyli inni , może to mieć znaczenie, gdy wywoływaniea=a
ma skutki uboczne, takie jak dodawanie kluczy do skrótu.a ||= b
⇔a = b unless a
??Zachowanie tych stwierdzeń różni się tylko tym, co oceniają, kiedy
a
jest zgodne z prawdą. W takim przypadkua = b unless a
oceni nanil
(choća
nadal nie zostanie ustawiony zgodnie z oczekiwaniami), podczas gdya ||= b
oceni naa
.a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????Nadal nie. Te instrukcje mogą się różnić, gdy
method_missing
istnieje metoda, która zwraca prawdziwą wartośća
. W takim przypadkua ||= b
oceni do jakichkolwiekmethod_missing
zwrotów, a nie spróbuje ustawića
, natomiastdefined?(a) ? (a || a = b) : (a = b)
ustawia
nab
i oceni dob
.Okej, okej, więc co jest
a ||= b
równoważne? Czy istnieje sposób na wyrażenie tego w Ruby?Zakładając, że niczego nie przeoczę , uważam, że
a ||= b
jest funkcjonalnie równoważny ... ( bęben )Czekaj! Czy to nie tylko pierwszy przykład z noopem przed nim? Cóż, niezupełnie. Pamiętasz, jak powiedziałem wcześniej, że nie
a ||= b
jest to równoważne tylkoa || a = b
wtedy, gdya
nieokreślona zmienna lokalna? Cóż,a = nil if false
zapewnia, żea
nigdy nie jest niezdefiniowany, nawet jeśli ta linia nigdy nie jest wykonywana. Zmienne lokalne w Ruby mają zasięg leksykalny.źródło
(a=b unless a) or a
a
jest metodą, zostanie wywołana dwa razy zamiast raz (jeśli za pierwszym razem zwróci prawdziwą wartość). Może to powodować różne zachowania, jeśli na przykłada
powrót zajmuje dużo czasu lub ma skutki uboczne.b
doa
, czy rhs wciąż przypisuje do lhs, czy innymi słowy, czy lhs nadal nie ustawia swojej wartości na rhs?a ||= b
odpowiedź, jaką znalazłem w Internecie. Dzięki.unless x x = y end
chyba że x ma wartość (nie jest zero ani fałsz), ustaw ją na wartość y
jest równa
x ||= y
źródło
Przypuszczać
a = 2
ib = 3
TO
a ||= b
zostanie obliczone naa
wartość tj2
.Tak jak wtedy, gdy ewaluacja do jakiejś wartości nie była wynikiem
false
lubnil
… Dlategoll
nie jest ocenianab
wartości.Teraz Załóżmy
a = nil
ib = 3
.Wtedy
a ||= b
będzie wynikał3
tjb
wartości „s.Gdy najpierw próbuje oszacować wartość, która spowodowała
nil
... więc to oszacowałb
wartość.Najlepszym przykładem zastosowanym w aplikacji ror jest:
Gdzie
User.find_by_id(session[:user_id])
jest odpalany wtedy i tylko wtedy, gdy@current_user
nie został wcześniej zainicjowany.źródło
a || = b
Oznacza, że jakakolwiek wartość jest obecna w „a” i nie chcesz jej zmieniać, zachowaj tę wartość, w przeciwnym razie, jeśli „a” nie ma żadnej wartości, użyj wartości „b”.
Proste słowa, jeśli lewa strona, jeśli nie jest pusta, wskazują na istniejącą wartość, w przeciwnym razie wskazują na wartość po prawej stronie.
źródło
jest równa
i nie
z powodu sytuacji, w której zdefiniujesz skrót z wartością domyślną (skrót zwróci wartość domyślną dla wszelkich niezdefiniowanych kluczy)
Jeśli użyjesz:
a jest nadal:
ale kiedy piszesz tak:
a staje się:
ponieważ przypisałeś sobie wartość klucza
10
, która domyślnie ma wartość true, więc hash jest teraz definiowany dla klucza10
, zamiast nigdy nie wykonywać przypisania w pierwszej kolejności.źródło
To jak leniwa instancja. Jeśli zmienna jest już zdefiniowana, pobierze tę wartość zamiast ponownie ją tworzyć.
źródło
Pamiętaj również, że
||=
nie jest to operacja atomowa, a zatem nie jest bezpieczna dla wątków. Zasadniczo nie używaj go do metod klasowych.źródło
To jest domyślna notacja przydziału
na przykład: x || = 1
sprawdzi, czy x jest zerowe, czy nie. Jeśli x rzeczywiście wynosi zero, wówczas przypisuje mu tę nową wartość (1 w naszym przykładzie)
dokładniej:
jeśli x == zero
x = 1
koniec
źródło
nil
albofalse
nie tylkonil
|| = jest operatorem przypisania warunkowego
jest równa
lub alternatywnie
źródło
Jeśli
X
NIE ma wartości, zostanie jej przypisana wartośćY
. W przeciwnym razie zachowa oryginalną wartość, 5 w tym przykładzie:źródło
Jako powszechne nieporozumienie
a ||= b
nie jest równoważnea = a || b
, ale zachowuje się jaka || a = b
.Ale nadchodzi trudna sprawa. Jeśli
a
nie jest zdefiniowany,a || a = 42
podnosiNameError
, aa ||= 42
wraca42
. Nie wydają się więc równoważnymi wyrażeniami.źródło
||=
przypisuje wartość do prawej tylko wtedy, gdy left == zero (lub jest niezdefiniowany lub fałszywy).źródło
Ta składnia ruby-lang. Prawidłowa odpowiedź to sprawdzenie dokumentacji ruby-lang. Wszystkie inne wyjaśnienia są zaciemnione .
Google
„ruby-lang docs Skrócone przypisanie”.
Dokumenty Ruby-lang
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
źródło
Ponieważ
a
został już ustawiony na1
Ponieważ
a
byłnil
źródło
To przekłada się na:
który będzie
więc w końcu
Teraz, jeśli zadzwonisz ponownie:
Teraz, jeśli zadzwonisz ponownie:
Jeśli zaobserwujesz,
b
wartość nie zostanie przypisanaa
.a
nadal będzie miał5
.Jest to wzorzec zapamiętywania używany w Ruby w celu przyspieszenia dostępu.
To w zasadzie przekłada się na:
Dlatego po raz pierwszy wywołasz tę metodę, wykonasz połączenie z bazą danych.
Przyszłe wywołania tej metody zwrócą tylko wartość
@users
zmiennej instancji.źródło
||=
nazywa się operatorem przypisania warunkowego.Zasadniczo działa,
=
ale z wyjątkiem tego, że jeśli zmienna została już przypisana , nic nie zrobi.Pierwszy przykład:
Drugi przykład:
W pierwszym przykładzie
x
jest teraz równy 10. Jednak w drugim przykładziex
jest już zdefiniowany jako 20. Zatem operator warunkowy nie ma wpływu.x
ma jeszcze 20 po uruchomieniux ||= 10
.źródło
a ||= b
to to samo co powiedzeniea = b if a.nil?
luba = b unless a
Ale czy wszystkie 3 opcje wykazują taką samą wydajność? Z Ruby 2.5.1 to
na moim komputerze trwa 0,099 sekundy
zajmuje 0,062 sekundy. To prawie 40% szybciej.
a następnie mamy również:
co zajmuje 0,166 sekundy.
Nie znaczy to, że ogólnie będzie to miało znaczący wpływ na wydajność, ale jeśli potrzebujesz ostatniej optymalizacji, rozważ ten wynik. Tak poza tym:
a = 1 unless a
jest łatwiejszy do odczytania dla nowicjusza, jest oczywisty.Uwaga 1: powodem wielokrotnego powtarzania linii przypisania jest zmniejszenie narzutu pętli w mierzonym czasie.
Uwaga 2: Wyniki są podobne, jeśli zrobię
a=nil
zero przed każdym zadaniem.źródło