Co oznacza następujący kod w Ruby?
||=
Czy ma to jakieś znaczenie lub przyczynę dla składni?
Co oznacza następujący kod w Ruby?
||=
Czy ma to jakieś znaczenie lub przyczynę dla składni?
Odpowiedzi:
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 ajest 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 ||= truema nie robić to, co twoja odpowiedź mówi, że robi „niuans”.
a ||= bjest operatorem przypisania warunkowego . Oznacza to, że jeśli ajest niezdefiniowany lub falsey , to oceń bi ustaw awynik . Odpowiednio, jeśli ajest zdefiniowane i ocenia zgodnie z prawdą, bto nie jest oceniane i nie ma miejsca przypisanie. Na przykład:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Myląco wygląda podobnie do innych operatorów przypisania (takich jak +=), ale zachowuje się inaczej.
a += b przetłumaczyć na a = a + ba ||= b z grubsza przekłada się na a || a = bJest to prawie skrót dla a || a = b. Różnica polega na tym, że gdy ajest niezdefiniowany, a || a = bpodnosi się NameError, a a ||= bustawia ana b. To rozróżnienie nie jest ważne, jeśli ai boba są zmiennymi lokalnymi, ale jest znaczące, jeśli jest metodą getter / setter klasy.
Dalsza lektura:
h = Hash.new(0); h[1] ||= 2. Rozważmy teraz dwa możliwe rozszerzenia h[1] = h[1] || 2vs h[1] || h[1] = 2. Oba wyrażenia oceniają na, 0ale pierwsze niepotrzebnie zwiększa rozmiar skrótu. Być może dlatego Matz postanowił sprawić, by ||=zachowywał się bardziej jak drugie rozszerzenie. (
a || a = bpodnosi NameErrorif ajest niezdefiniowane. a ||= bnie, ale zamiast tego inicjuje ai ustawia na b. To jedyne rozróżnienie między tymi dwoma, o ile mi wiadomo. Podobnie, jedyną różnicą między a = a || bi a ||= b, o której jestem świadomy, jest to, że jeśli a=metoda jest wywoływana, niezależnie od tego, co azwróci. Poza tym jedyną różnicą między mną, a = b unless aa a ||= btym, o czym jestem świadomy, jest to, że to stwierdzenie sprawdza, czy nilzamiast jest prawdziwe. Wiele przybliżeń, ale nic zupełnie równoważnego ...aa
a ||= b
ocenia w taki sam sposób, jak każdy z poniższych wierszy
a || a = b
a ? a : a = b
if a then a else a = b end
-
Z drugiej strony,
a = a || b
ocenia w taki sam sposób, jak każdy z poniższych wierszy
a = a ? a : b
if a then a = a else a = b end
-
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.
afalse = 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, I if a then a = a else a = b endwygeneruje błąd, jeśli ajest niezdefiniowana, natomiast a ||= bi a = a || bnie będzie. Ponadto a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : b, i if a then a = a else a = b endocenić adwukrotnie gdy ajest truthy, natomiast a ||= bi a = a || bnie.
a || a = bnie będzie oceniać adwukrotnie, kiedy ajest to prawda.
the end state will be equivalent after the whole line has been evaluatedTo niekoniecznie prawda. Co jeśli ajest metodą? Metody mogą mieć skutki uboczne. Np public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5, self.a ||= bpowróci 6, ale self.a ? self.a : self.a = bpowróci 7.
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:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
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.
undefined, ale także włącza falsei nil, co może nie mieć znaczenia current_user, ale szczególnie falsemoże być nieoczekiwana w innych przypadkach
x ||= y
jest
x || x = y
„jeśli x jest fałszywe lub niezdefiniowane, to x wskazuje na y”
Mówiąc dokładniej, a ||= boznacza „jeśli ajest niezdefiniowany lub fałszywy ( falselub nil), ustaw ana bi oceń na (tj. Zwróć ) b, w przeciwnym razie oceń na a”.
Inni często próbują to zilustrować, mówiąc, że a ||= bjest to równoważne z a || a = blub a = 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 ajest niezdefiniowaną zmienną lokalną. W takim przypadku a ||= bustawi się ana b(i ocenia na b), podczas gdy a || a = bpodniesie NameError: 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ć, gdy a=jest metodą na obiekcie i ajest prawdziwa. W takim przypadku a ||= bnie zrobi nic (prócz oceny a), a a = a || bzadzwoni a=(a)do aodbiornika. Jak zauważyli inni , może to mieć znaczenie, gdy wywoływanie a=ama 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 ajest zgodne z prawdą. W takim przypadku a = b unless aoceni na nil(choć anadal nie zostanie ustawiony zgodnie z oczekiwaniami), podczas gdy a ||= boceni na a.
a ||= b ⇔ defined?(a) ? (a || a = b) : (a = b) ????
Nadal nie. Te instrukcje mogą się różnić, gdy method_missingistnieje metoda, która zwraca prawdziwą wartość a. W takim przypadku a ||= boceni do jakichkolwiek method_missingzwrotów, a nie spróbuje ustawić a, natomiast defined?(a) ? (a || a = b) : (a = b)ustawi ana bi oceni do b.
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 ||= bjest funkcjonalnie równoważny ... ( bęben )
begin
a = nil if false
a || a = b
end
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 ||= bjest to równoważne tylko a || a = bwtedy, gdy anieokreślona zmienna lokalna? Cóż, a = nil if falsezapewnia, że anigdy nie jest niezdefiniowany, nawet jeśli ta linia nigdy nie jest wykonywana. Zmienne lokalne w Ruby mają zasięg leksykalny.
(a=b unless a) or a
ajest 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ład apowrót zajmuje dużo czasu lub ma skutki uboczne.
bdoa , czy rhs wciąż przypisuje do lhs, czy innymi słowy, czy lhs nadal nie ustawia swojej wartości na rhs?
a ||= bodpowiedź, jaką znalazłem w Internecie. Dzięki.
Przypuszczać a = 2 ib = 3
TO a ||= b zostanie obliczone na awartość tj2 .
Tak jak wtedy, gdy ewaluacja do jakiejś wartości nie była wynikiem falselub nil… Dlatego llnie jest ocenianab wartości.
Teraz Załóżmy a = nilib = 3 .
Wtedy a ||= bbędzie wynikał 3tjb 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:
#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
Gdzie User.find_by_id(session[:user_id])jest odpalany wtedy i tylko wtedy, gdy @current_usernie został wcześniej zainicjowany.
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.
a ||= b
jest równa
a || a = b
i nie
a = a || b
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)
a = Hash.new(true) #Which is: {}
Jeśli użyjesz:
a[10] ||= 10 #same as a[10] || a[10] = 10
a jest nadal:
{}
ale kiedy piszesz tak:
a[10] = a[10] || 10
a staje się:
{10 => true}
ponieważ przypisałeś sobie wartość klucza 10, która domyślnie ma wartość true, więc hash jest teraz definiowany dla klucza 10, zamiast nigdy nie wykonywać przypisania w pierwszej kolejności.
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.
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
nilalbo falsenie tylkonil
Jeśli XNIE ma wartości, zostanie jej przypisana wartość Y. W przeciwnym razie zachowa oryginalną wartość, 5 w tym przykładzie:
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
||= przypisuje wartość do prawej tylko wtedy, gdy left == zero (lub jest niezdefiniowany lub fałszywy).
Ta składnia ruby-lang. Prawidłowa odpowiedź to sprawdzenie dokumentacji ruby-lang. Wszystkie inne wyjaśnienia są zaciemnione .
„ruby-lang docs Skrócone przypisanie”.
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
To przekłada się na:
a = a || b
który będzie
a = nil || 5
więc w końcu
a = 5
Teraz, jeśli zadzwonisz ponownie:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Teraz, jeśli zadzwonisz ponownie:
a ||= b
a = a || b
a = 5 || 6
a = 5
Jeśli zaobserwujesz, bwartość nie zostanie przypisana a. anadal będzie miał 5.
Jest to wzorzec zapamiętywania używany w Ruby w celu przyspieszenia dostępu.
def users
@users ||= User.all
end
To w zasadzie przekłada się na:
@users = @users || User.all
Dlatego po raz pierwszy wywołasz tę metodę, wykonasz połączenie z bazą danych.
Przyszłe wywołania tej metody zwrócą tylko wartość @userszmiennej instancji.
||= 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:
x ||= 10
Drugi przykład:
x = 20
x ||= 10
W pierwszym przykładzie xjest teraz równy 10. Jednak w drugim przykładzie xjest już zdefiniowany jako 20. Zatem operator warunkowy nie ma wpływu. xma jeszcze 20 po uruchomieniu x ||= 10.
a ||= bto to samo co powiedzenie a = b if a.nil?luba = b unless a
Ale czy wszystkie 3 opcje wykazują taką samą wydajność? Z Ruby 2.5.1 to
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
na moim komputerze trwa 0,099 sekundy
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
zajmuje 0,062 sekundy. To prawie 40% szybciej.
a następnie mamy również:
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
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=nilzero przed każdym zadaniem.