|| = (veya-eşittir) Ruby'de ne anlama geliyor?


340

Aşağıdaki kod Ruby'de ne anlama geliyor?

||=

Sözdiziminin herhangi bir anlamı veya nedeni var mı?

Yanıtlar:


175

Bu soru Ruby posta listelerinde ve Ruby bloglarında o kadar sık ​​tartışılmıştır ki şimdi Ruby posta listesinde tek amacı bu sorunu tartışan Ruby posta listesindeki diğer tüm konulara bağlantılar toplamaktır. .

İşte bir tane: || = (VEYA Eşit) konuların ve sayfaların kesin listesi

Eğer varsa gerçekten ne olup bittiğini bilmek istiyorum, bir "Kısaltılmış atamaları" Bölüm 11.4.2.3 bakmak Yakut Dili Taslak Şartname .

İlk yaklaşım olarak,

a ||= b

eşittir

a || a = b

ve eşdeğer değil

a = a || b

Ancak bu, özellikle atanımsızsa , sadece bir ilk yaklaşımdır . Anlambilim ayrıca, basit bir değişken atama, yöntem atama veya dizin oluşturma atama olmasına bağlı olarak da değişir:

a    ||= b
a.c  ||= b
a[c] ||= b

hepsi farklı muamele görüyor.


2
İkinci bağlantı bit çürümesinden muzdaripti (meta'dan stackoverflow.com/users/540162/nightfirecat tarafından yorumlandı ).
Andrew Grimm

331
Bu çok şifreli bir cevap değil. Kısa cevap şu şekilde gözükmektedir: a || = b, a tanımsızsa b değerini verin, aksi takdirde yalnız bırakın. (Tamam, nüanslar ve özel durumlar var, ancak bu temel durum.)
Steve Bennett

20
@SteveBennett: Ben aslında demem a = false; a ||= truegelmez değil Cevabınız bir "nüans" yapar dediğini yap.
Jörg W Mittag

23
Belki bu soru birçok kez sorulmuştur, çünkü insanlar bu soruya birçok kez sorulduğu konusunda cevap vermeye devam etmektedir.
einnocent

8
Bu cevap ile neden birden fazla iş parçacığı olduğunu görmek kolaydır. Acemi bir şapka kullanarak bu soruya bir cevap aramaya çalışırsanız, tüm cevapların net olmadığını fark edeceksiniz. Örneğin, bununla sadece olmayanı söylüyorsunuz. Cevabınızı geliştirmenizi ve acemiler için kolay cevaplar vermenizi öneririm: a = b sürece a
Arnold Roa

594

a ||= ba, koşullu atama operatörü . Bunun anlamı , eğer atanımlanmamış veya Falsey , daha sonra değerlendirmek bve set asonuca . Eşdeğer olarak, adoğruluk için tanımlanır ve değerlendirilirse, o zaman bdeğerlendirilmez ve hiçbir ödev gerçekleşmez. Örneğin:

a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0

foo = false # => false
foo ||= true # => true
foo ||= false # => true

Şaşırtıcı bir şekilde, diğer atama operatörlerine (örneğin +=) benzer, ancak farklı davranır.

  • a += b Çevirir a = a + b
  • a ||= b kabaca çevirir a || a = b

Bunun için kestirme bir yöntemdir a || a = b. Fark ne zaman, yani atanımsız, a || a = bgetireceğini NameErroroysa a ||= bsetleri aiçin b. Bu ayrım, eğer önemsizdir ave bhem yerel hem de değişken olan, ancak, ya bir sınıfın bir alıcı / ayarlayıcı yöntemi ise anlamlıdır.

Daha fazla okuma:


52
Bu cevap için teşekkür ederim, çok daha mantıklı.
Tom Hert

sığınak yeterince aradı ama neden a = a || b. belki sadece benim kişisel görüşüm ama böyle bir nüans var biraz saçma ...
dtc

2
@dtc, düşünün h = Hash.new(0); h[1] ||= 2. Şimdi h[1] = h[1] || 2vs iki olası genişletmeleri düşünün h[1] || h[1] = 2. Her iki ifade de değerlendirilir, 0ancak ilki gereksiz yere karma boyutunu artırır. Belki de bu yüzden Matz ||=ikinci genişleme gibi davranmayı seçti . (Bunu başka bir cevapta bağlantılı olan konuların
birinden aldım

1
Ne kadar derinlemesine gittiğine dair diğer cevabı seviyorum, ama bu cevabı basitliği için seviyorum. Ruby'yi öğrenen biri için bu ihtiyacımız olan cevap türüdür. || = 'nin ne anlama geldiğini bilseydik, soru muhtemelen farklı şekillerde ifade edilirdi.
OBCENEIKON

1
Fyi, tanımsızsa a || a = bbir NameErrorif yükseltir a. a ||= bdeğil, başlangıç adurumuna getirir ve olarak ayarlar b. Bildiğim kadarıyla ikisi arasındaki tek fark bu. Benzer şekilde, farkında olduğum a = a || bve a ||= bfark ettiğim tek fark , eğer a=bir yöntem ise, ne adöndürürse döndürsün çağrılır . Ayrıca arasındaki tek fark a = b unless ave a ||= bBildiğim kadarıyla buna ifadesi değerlendirir olmasıdır nilyerine aeğer atruthy olduğunu. Yaklaşık bir sürü, ama oldukça eşdeğer bir şey yok ...
Ajedi32

32

Kısa ve eksiksiz cevap

a ||= b

aşağıdaki satırların her biri ile aynı şekilde değerlendirir

a || a = b
a ? a : a = b
if a then a else a = b end

-

Diğer yandan,

a = a || b

aşağıdaki satırların her biri ile aynı şekilde değerlendirir

a = a ? a : b
if a then a = a else a = b end

-

Düzenleme: AJedi32 yorumlarda belirtildiği gibi, bu sadece şu geçerli tutar: 1. a tanımlı bir değişkendir. 2. Bir kez ve iki kez değerlendirmek, program veya sistem durumunda bir fark yaratmaz.


1
emin misin? Bu a, false / zero / undefined ise, iki kez değerlendirildiğini gösterir. (Ama Ruby'yi bilmiyorum, bu yüzden değerlerin tam olarak 'değerlendirilebileceğini' bilmiyorum ...)
Steve Bennett

Ne dediğini anlıyorum. İki satırın eşdeğer olması demek istediğim, son durumun tüm satır değerlendirildikten sonra eşdeğer olacağı, yani a, b'nin değeri ve döndürülen değer anlamına gelir. Yakut tercümanların oraya ulaşmak için farklı durumların - a'nın çeşitli değerlendirmeleri gibi - kullanılıp kullanılmayacağı tamamen mümkündür. Orada yakut tercüman uzmanı var mı?
the_minted

3
Bu pek doğru değil. a || a = b, a ? a : a = b, if a then a else a = b endVe if a then a = a else a = b endeğer bir hata atar atanımlanmamış, oysa a ||= bve a = a || bbunu yapmaz. Ayrıca a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : b, ve if a then a = a else a = b enddeğerlendirmek aiki kez ne zaman atruthy olduğunu oysa a ||= bve a = a || byapma.
Ajedi32

1
* düzeltme: doğru olduğunda iki kez a || a = bdeğerlendirmez . aa
Ajedi32

1
@the_minted the end state will be equivalent after the whole line has been evaluatedBu mutlaka doğru olmayabilir. Ya abir yöntem? Yöntemlerin yan etkileri olabilir. Örneğin İle public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5, self.a ||= b6 geri döner, ancak self.a ? self.a : self.a = b7 geri döner.
Ajedi32

27

Kısacası, içinde a||=bvasıtası: Eğer abir undefined, nil or false, atama biçin a. Aksi takdirde, asağlam tutun .


16
Temel olarak,


x ||= y anlamına geliyor

eğer xherhangi bir değeri vardır aksi set, yalnız bırakın ve değerini değiştirmez xiçiny


13

Anlamına gelir veya eşittir. Soldaki değerin tanımlanıp tanımlanmadığını kontrol eder, sonra bunu kullanın. Değilse, sağdaki değeri kullanın. Modellerde örnek değişkenleri önbelleğe almak için Rails'te kullanabilirsiniz.

Oturum açmış olan kullanıcıyı getirmek için bir işlev oluşturduğumuz hızlı Rails tabanlı bir örnek:

class User > ActiveRecord::Base

  def current_user
    @current_user ||= User.find_by_id(session[:user_id])
  end

end

@Current_user örnek değişkeninin ayarlanıp ayarlanmadığını kontrol eder. Öyleyse, döndürür ve böylece bir veritabanı çağrısını kaydeder. Ancak ayarlanmazsa, çağrıyı yaparız ve sonra @current_user değişkenini buna ayarlarız. Bu gerçekten basit bir önbellekleme tekniğidir, ancak aynı örnek değişkenini uygulamada birden çok kez aldığınızda mükemmeldir.


8
Bu yanlış. Lütfen Ruby-Forum.Com/topic/151660 adresini ve burada sağlanan bağlantıları okuyun .
Jörg W Mittag

1
@Jo (umlaut) rg, neyin yanlış olduğunu görmüyorum. Bağlantınız diğer bağlantıların bir listesidir. Bunun neden yanlış olduğuna dair gerçek bir açıklama yok, sadece sonunda bir değer yargısı gibi geliyor.
Eggmatters

bu cevap yanlıştır, çünkü sadece tetiklenmez undefined, aynı zamanda falseve nilbununla ilgili olmayabilir ve current_userözellikle falsediğer durumlarda beklenmedik olabilir
dfherr

Bu yanıtın gösteremediği her ne kadar eksikliğe rağmen (nil / false için çalışmıyor), neden || = kullanmak istediğinizi açıklayan ilk kişi budur, teşekkür ederim!
Jonathan Tuzman


8

Kesin olarak a ||= b"belirtilen yöntemler atanımlanmamış ya da (falsy olan falseya da nil), set aiçin bve (diğer bir deyişle dönüş) değerlendirmek bbaşka bir şekilde değerlendirilmesi, a".

Diğerleri a ||= bbunu a || a = bveya ile eşdeğer diyerek bunu göstermeye çalışırlar a = a || b. Bu eşdeğerleri konsepti anlamak için yararlı olabilir, ancak bunlar farkında olabilir değil her koşulda doğru. Açıklamama izin ver:

  • a ||= ba || a = b ?

    Bu ifadelerin davranışı, atanımsız bir yerel değişken olduğunda farklılık gösterir . Bu durumda, a ||= bkoyacaktır aiçin b(ve değerlendirmek boysa) a || a = bolacak zam NameError: undefined local variable or method 'a' for main:Object.

  • a ||= ba = a || b ?

    Bu ifadelerin denklik çoğu zaman benzer bir denklik diğeri için de geçerlidir, çünkü varsayılır kısaltılmış atama operatörleri (yani +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, ve >>=). Ancak, için ||=bu tabloların davranış olabilir zaman farklı a=bir nesne üzerinde bir yöntem olup, atruthy olup. Bu durumda, a ||= b(hiç değerlendirmek başka bir şey yapacak a, oysa) a = a || barayacak a=(a)üzerinde a'in alıcısı. Diğerlerinin de belirttiği gibi , bu arama yaparken a=abir karmaya anahtar eklemek gibi yan etkileri olduğunda fark yaratabilir .

  • a ||= ba = b unless a ??

    Bu ifadelerin davranışı, yalnızca doğru olduğunda ne değerlendirdiklerine agöre değişir. Bu durumda, a = b unless adeğerlendirilecek nil(yine ade beklendiği gibi ayarlanmayacak), oysa a ||= bdeğerlendirecektir a.

  • a ||= bdefined?(a) ? (a || a = b) : (a = b) ????

    Hala hayır. Bu ifadeler, method_missingdoğruluk değeri döndüren bir yöntem bulunduğunda farklılık gösterebilir a. Bu durumda, a ||= bher ne kadar değerlendirecek method_missingdönüşleri ve sete denemez a, oysa defined?(a) ? (a || a = b) : (a = b)belirleyecektir aiçin bve değerlendirmek b.

Tamam, tamam, peki olduğu a ||= b eşdeğer? Bunu Ruby'de ifade etmenin bir yolu var mı?

Eh, ben inanıyorum, ben bir şey bakan değilim varsayarak a ||= b(... işlevsel eşdeğerdir Trampet )

begin
  a = nil if false
  a || a = b
end

Tut! Bundan önce noop olan ilk örnek değil mi? Pek iyi değil. Daha önce nasıl söylediğimi hatırlıyorum a ||= b, ancak tanımlanmamış bir yerel değişkenin a || a = bne zaman eşdeğer olmadığını a? Eh, a = nil if falseolmasını sağlar abu hat asla çalıştırılmaz halde tanımsız asla. Ruby'deki yerel değişkenler sözlüksel olarak kapsamlıdır.


Genişletilmiş üçüncü örneğiniz:(a=b unless a) or a
vol7ron

1
@ vol7ron # 2 ile benzer bir sorunu var. Eğer abir yöntemdir, bunun yerine (bir truthy değerini ilk kez dönerse) bir kez iki kez çağrılır. Örneğin, ageri dönmesi uzun sürerse veya yan etkileri varsa davranışların farklı olmasına neden olabilir .
Ajedi32

Ayrıca, ilk cümle, söylemek gerekir atama biçina sol eksen hala rhs'sine değerini belirlemez, rhs'si hala lhs atamak değil veya başka bir deyişle?
vol7ron

a ||= bİnternette bulduğum en iyi cevap. Teşekkürler.
Eric Duminil

3

unless x x = y end

x'in bir değeri yoksa (nil veya false değildir), y'ye eşit olarak ayarlayın

eşittir

x ||= y


3

Varsayalım a = 2veb = 3

O zaman, yani değeri a ||= b ile sonuçlanacaktır .a2

Bir değerle sonuçlanmadığı zaman ortaya çıkan falseya da nil.. Bu yüzden değerini lldeğerlendirmez b.

Şimdi varsayalım a = nilve b = 3.

O a ||= bzaman 3yani b'değeri ile sonuçlanacaktır .

İlk olarak a'nın değerini, nilyani bunun değerini değerlendirmeyi denedi b.

Ror uygulamasında kullanılan en iyi örnek:

#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

Nerede, User.find_by_id(session[:user_id])yalnızca ve @current_userdaha önce başlatılmazsa tetiklenir .


3

a || = b

'A' içinde herhangi bir değerin olup olmadığını ve bu değeri kullanarak keep değerini değiştirmek istemediğinizi belirtir, aksi takdirde 'a' herhangi bir değere sahip değilse, 'b' değerini kullanın.

Basit kelimeler, eğer sol taraf boş değilse, mevcut değere, diğer taraf sağ taraftaki değere işaret eder.


2
a ||= b

eşittir

a || a = b

ve yok

a = a || b

varsayılan değeri olan bir karma tanımladığınız durum nedeniyle (karma tanımsız anahtarlar için varsayılan değeri döndürür)

a = Hash.new(true) #Which is: {}

Eğer kullanırsan:

a[10] ||= 10 #same as a[10] || a[10] = 10

a hala:

{}

ama böyle yazdığınızda:

a[10] = a[10] || 10

a olur:

{10 => true}

çünkü 10varsayılan olarak true değerine ayarlanmış olan anahtarın kendisinin değerini atamış olursunuz , öyleyse artık 10ödevi asla yerine getirmek yerine anahtar için tanımlanmıştır .


2

Tembel bir örnekleme gibi. Değişken zaten tanımlanmışsa, değeri yeniden oluşturmak yerine o değeri alacaktır.


2

Lütfen ||=bunun bir atomik işlem olmadığını ve bu nedenle iş parçacığı için güvenli olmadığını unutmayın. Genel kural olarak, sınıf yöntemleri için kullanmayın.


2

Bu varsayılan atama gösterimi

örneğin: x || = 1
bu, x'in sıfır olup olmadığını kontrol eder. X gerçekten sıfır ise, o zaman bu yeni değeri atar (örneğimizde 1)

daha açık:
if x == nil
x = 1
son


ya nilda false, sadecenil
Alex Poca

2

|| = a, koşullu atama operatörü

  x ||= y

eşittir

  x = x || y

Veya alternatif olarak

if defined?(x) and x
    x = x
else 
    x = y
end

2

Eğer Xbir değere sahip DEĞİLDİR, bu değeri atanacaktır Y. Aksi takdirde, bu örnekte 5 olan orijinal değerini koruyacaktır:

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

1

Yaygın bir yanlış anlama olarak, a ||= beşdeğer değildir a = a || b, ancak gibi davranır a || a = b.

Ama burada zor bir dava geliyor. Eğer a, tanımlanmamış a || a = 42zam NameErroryaparken, a ||= 42getiriler 42. Yani, eşdeğer ifadeler gibi görünmüyorlar.


1

||= yalnızca left == nil (veya undefined veya false ise) sağa değer atar.


muhtemelen sağ yerine 'sola değer atar' demek
istediniz


0
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1

Çünkü azaten1

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

Çünkü aidinil


Burada cevaplanan tarih nedir. Neden yıl göstermiyor?
Shiv

0
b = 5
a ||= b

Bu şu anlama gelir:

a = a || b

hangisi olacak

a = nil || 5

en sonunda

a = 5

Şimdi tekrar ararsanız:

a ||= b
a = a || b
a = 5 || 5
a = 5

b = 6

Şimdi tekrar ararsanız:

a ||= b
a = a || b
a = 5 || 6
a = 5 

Eğer gözlemlerseniz, bdeğer atanmaz a. ahala olacak 5.

Accesso'ları hızlandırmak için Ruby'de kullanılan bir Memoization Kalıbı.

def users
  @users ||= User.all
end

Bu temelde şu anlama gelir:

@users = @users || User.all

Bu nedenle, bu yöntemi ilk çağırdığınızda veritabanına bir çağrı yapacaksınız.

Bu yönteme gelecekteki çağrılar @usersörnek değişkeninin değerini döndürecektir .


0

||= koşullu atama operatörü olarak adlandırılır.

Temel olarak çalışır, =ancak bir değişken önceden atanmışsa hiçbir şey yapmayacaktır.

İlk örnek:

x ||= 10

İkinci örnek:

x = 20
x ||= 10

İlk örnekte xşimdi 10'a eşittir. Ancak, ikinci örnekte xzaten 20 olarak tanımlanmıştır. Dolayısıyla koşullu işlecin bir etkisi yoktur. xkoştuktan sonra hala 20 yaşında x ||= 10.


-2

a ||= bdemekle aynı a = b if a.nil?veyaa = b unless a

Ancak 3 seçeneğin tümü aynı performansı gösteriyor mu? Ruby 2.5.1 ile bu

1000000.times do
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
end

bilgisayarımda 0.099 saniye sürüyor

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

0.062 Saniye sürer. Bu neredeyse% 40 daha hızlı.

ve sonra:

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

0.166 Saniye sürüyor.

Bunun genel olarak önemli bir performans etkisi yaratacağı değil, ancak son optimizasyon bitine ihtiyacınız varsa, bu sonucu göz önünde bulundurun. Bu arada: a = 1 unless aacemi için okunması daha kolaydır, açıklayıcıdır.

Not 1: Atama satırını birden çok kez tekrar etmenin nedeni, ölçülen sürede döngü yükünü azaltmaktır.

Not 2: a=nilHer atamadan önce sıfır yaparsam sonuçlar benzerdir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.