Apa artinya || = (atau-sama dengan) di Ruby?


340

Apa arti kode berikut di Ruby?

||=

Apakah ada arti atau alasan untuk sintaksis?

Jawaban:


175

Pertanyaan ini telah sering dibahas di milis-rubel Ruby dan blog-blog Ruby sehingga sekarang ada utas-utas pada milis-Ruby yang satu-satunya tujuan adalah untuk mengumpulkan tautan ke semua utas lainnya pada milis-Ruby yang membahas masalah ini .

Ini dia: Daftar pasti utas dan halaman || = (OR Sama)

Jika Anda benar - benar ingin tahu apa yang sedang terjadi, lihat Bagian 11.4.2.3 "Penugasan singkat" dari Spesifikasi Draf Bahasa Ruby .

Sebagai perkiraan pertama,

a ||= b

setara dengan

a || a = b

dan tidak setara dengan

a = a || b

Namun, itu hanya perkiraan pertama, terutama jika atidak terdefinisi. Semantik juga berbeda tergantung pada apakah itu tugas variabel sederhana, tugas metode atau tugas pengindeksan:

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

semua diperlakukan berbeda.


2
Tautan kedua menderita bit rot (komentar dari meta oleh stackoverflow.com/users/540162/nightfirecat ).
Andrew Grimm

330
Itu adalah jawaban yang sangat samar. Jawaban singkatnya adalah: a || = b berarti, jika a tidak terdefinisi maka berikan nilai b, jika tidak biarkan saja. (Ok, ada nuansa dan kasus khusus, tapi itulah kasus dasarnya.)
Steve Bennett

20
@SteveBennett: Saya tidak akan menyebut fakta bahwa a = false; a ||= truetidak tidak melakukan apa jawaban Anda mengatakan itu melakukan "nuansa".
Jörg W Mittag

23
Mungkin pertanyaan ini telah ditanyakan berkali-kali karena orang terus merespons bahwa pertanyaan ini telah ditanyakan berkali-kali.
bersalah

8
Dengan jawaban ini mudah untuk melihat mengapa ada banyak utas. Jika Anda mencoba mencari jawaban untuk pertanyaan ini menggunakan topi pemula, Anda akan melihat bahwa semua jawaban tidak jelas. Misalnya, dengan yang ini Anda hanya mengatakan apa yang tidak. Saya sarankan untuk meningkatkan jawaban Anda dan memberikan jawaban yang mudah untuk pemula: a = b kecuali a
Arnold Roa

594

a ||= badalah operator penugasan bersyarat . Ini berarti jika atidak terdefinisi atau palsu , maka evaluasi bdan atur ahasilnya . Secara setara, jika adidefinisikan dan dievaluasi untuk kebenaran, maka btidak dievaluasi, dan tidak ada penugasan yang terjadi. Sebagai contoh:

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

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

Yang membingungkan, tampilannya mirip dengan operator penugasan lain (seperti +=), tetapi berperilaku berbeda.

  • a += b diterjemahkan menjadi a = a + b
  • a ||= b diterjemahkan secara kasar menjadi a || a = b

Ini adalah singkatan untuk a || a = b. Perbedaannya adalah bahwa, ketika atidak ditentukan, a || a = bakan dinaikkan NameError, sedangkan a ||= bset ake b. Perbedaan ini tidak penting jika adan bkeduanya merupakan variabel lokal, tetapi signifikan jika keduanya merupakan metode pengambil / penyetel suatu kelas.

Bacaan lebih lanjut:


52
Terima kasih atas jawaban ini, itu jauh lebih masuk akal.
Tom Hert

belum cukup mencari tetapi masih belum mengerti mengapa Anda akan menggunakan ini sebagai lawan a = a || b. mungkin hanya pendapat pribadi saya tetapi sedikit konyol bahwa nuansa seperti itu ada ...
dtc

2
@dtc, pertimbangkan h = Hash.new(0); h[1] ||= 2. Sekarang perhatikan dua ekspansi yang mungkin h[1] = h[1] || 2vs h[1] || h[1] = 2. Kedua ekspresi mengevaluasi 0tetapi yang pertama tidak perlu meningkatkan ukuran hash. Mungkin itu sebabnya Matz memilih untuk ||=bersikap lebih seperti ekspansi kedua. (Saya mendasarkan ini pada contoh dari salah satu utas yang ditautkan dalam jawaban lain.)
antinome

1
Saya suka jawaban lain untuk seberapa mendalamnya, tapi saya suka jawaban ini karena kesederhanaannya. Untuk seseorang yang belajar Ruby, ini adalah jenis jawaban yang kita butuhkan. Jika kita tahu apa artinya || =, maka pertanyaannya mungkin akan berbeda.
OBCENEIKON

1
Fyi, a || a = bmemunculkan NameErrorjika atidak terdefinisi. a ||= btidak, melainkan menginisialisasi adan mengaturnya b. Itulah satu-satunya perbedaan antara keduanya sejauh yang saya tahu. Demikian pula, satu-satunya perbedaan antara a = a || bdan a ||= byang saya sadari adalah bahwa jika a=metode, itu akan dipanggil terlepas dari apa yang akembali. Juga, satu-satunya perbedaan antara a = b unless adan a ||= byang saya sadari adalah bahwa pernyataan itu dievaluasi nildaripada ajika aitu benar. Banyak perkiraan, tapi tidak ada yang setara ...
Ajedi32

32

Jawaban singkat dan lengkap

a ||= b

mengevaluasi cara yang sama seperti masing - masing baris berikut

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

-

Di samping itu,

a = a || b

mengevaluasi cara yang sama seperti masing - masing baris berikut

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

-

Sunting: Seperti yang AJedi32 tunjukkan dalam komentar, ini hanya berlaku jika: 1. a adalah variabel yang ditentukan. 2. Mengevaluasi satu kali dan dua kali tidak menghasilkan perbedaan dalam kondisi program atau sistem.


1
Apakah Anda yakin? Ini menyiratkan bahwa jika asalah / nol / tidak terdefinisi, itu dievaluasi dua kali. (Tapi saya tidak tahu Ruby, jadi saya tidak tahu apakah nilai-nilai dapat 'dievaluasi' persis ...)
Steve Bennett

Saya mengerti apa yang Anda katakan. Yang saya maksudkan dengan dua baris yang ekuivalen adalah bahwa status akhir akan setara setelah seluruh baris dievaluasi, artinya nilai a, b dan apa yang dikembalikan. Apakah penerjemah ruby ​​menggunakan status yang berbeda - seperti beberapa evaluasi a - untuk sampai ke sana, sepenuhnya mungkin. Adakah ahli penerjemah ruby ​​di luar sana?
the_minted

3
Ini tidak benar. a || a = b, a ? a : a = b, if a then a else a = b end, Dan if a then a = a else a = b endakan melemparkan kesalahan jika atidak terdefinisi, sedangkan a ||= bdan a = a || btidak akan. Juga, a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : b, dan if a then a = a else a = b endmengevaluasi adua kali ketika aadalah truthy, sedangkan a ||= bdan a = a || btidak.
Ajedi32

1
* koreksi: a || a = btidak akan mengevaluasi adua kali kapan abenar.
Ajedi32

1
@the_minted the end state will be equivalent after the whole line has been evaluatedItu belum tentu benar. Bagaimana jika asuatu metode? Metode dapat memiliki efek samping. Misalnya Dengan public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5, self.a ||= bakan mengembalikan 6, tetapi self.a ? self.a : self.a = bakan kembali 7.
Ajedi32

27

Singkatnya, a||=bberarti: Jika aada undefined, nil or false, tetapkan bke a. Kalau tidak, tetaplah autuh.


16
Pada dasarnya,


x ||= y cara

jika xmemiliki nilai, biarkan saja dan jangan ubah nilainya, jika tidak, setel xkey


13

Artinya atau sama dengan. Ia memeriksa untuk melihat apakah nilai di sebelah kiri didefinisikan, lalu gunakan itu. Jika tidak, gunakan nilai di sebelah kanan. Anda dapat menggunakannya di Rails untuk menyimpan variabel instance dalam model.

Contoh cepat berbasis Rails, tempat kami membuat fungsi untuk mengambil pengguna yang saat ini masuk:

class User > ActiveRecord::Base

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

end

Itu memeriksa untuk melihat apakah variabel instance @current_user diatur. Jika ya, itu akan mengembalikannya, sehingga menghemat panggilan basis data. Namun jika tidak disetel, kami melakukan panggilan lalu mengatur variabel @current_user ke sana. Ini adalah teknik caching yang sangat sederhana tetapi bagus untuk ketika Anda mengambil variabel instance yang sama di aplikasi beberapa kali.


8
Ini salah. Silakan baca Ruby-Forum.Com/topic/151660 dan tautan yang disediakan di sini.
Jörg W Mittag

1
@ Jo (umlaut) rg, saya tidak melihat apa yang salah tentang itu. Tautan Anda adalah daftar tautan lain. Tidak ada penjelasan nyata mengapa itu salah, hanya terdengar seperti penilaian nilai di pihak Anda.
Eggmatters

jawaban ini salah, karena tidak hanya memicu undefined, tetapi juga pada falsedan nil, yang mungkin tidak relevan untuk current_user, tetapi terutama falsedapat tidak diharapkan dalam kasus lain
dfherr

Terlepas dari ketidaklengkapan apa pun yang ditunjukkan oleh jawaban ini (tidak berfungsi untuk nol / salah), ini adalah yang pertama menjelaskan mengapa Anda ingin menggunakan || =, jadi terima kasih!
Jonathan Tuzman


8

Tepatnya, a ||= bberarti "jika atidak terdefinisi atau palsu ( falseatau nil), diatur ake bdan dievaluasi ke (yaitu kembali) b, jika tidak mengevaluasi kea ".

Orang lain sering mencoba menggambarkan ini dengan mengatakan bahwa a ||= bitu setara dengan a || a = batau a = a || b. Kesetaraan ini dapat membantu untuk memahami konsep, tetapi perlu diketahui bahwa mereka tidak akurat dalam semua kondisi. Izinkan saya menjelaskan:

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

    Perilaku pernyataan ini berbeda ketika avariabel lokal tidak ditentukan. Dalam hal ini, a ||= bakan diatur ake b(dan dievaluasi ke b), sedangkan a || a = bakan meningkat NameError: undefined local variable or method 'a' for main:Object.

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

    The kesetaraan laporan ini sering diasumsikan, karena kesetaraan yang sama berlaku untuk lainnya tugas disingkat operator (yaitu +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, dan >>=). Namun, untuk ||=perilaku pernyataan-pernyataan ini dapat berbeda kapan a=metode pada suatu objek dan abenar. Dalam hal ini, a ||= bakan melakukan apa-apa (selain mengevaluasi a), sedangkan a = a || bakan memanggil a=(a)pada a's penerima. Seperti yang telah ditunjukkan orang lain , ini dapat membuat perbedaan ketika menelepon a=amemiliki efek samping, seperti menambahkan kunci ke hash.

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

    Perilaku pernyataan ini hanya berbeda dalam apa yang mereka evaluasi ketika abenar. Dalam hal ini, a = b unless aakan mengevaluasi ke nil(meskipun amasih tidak akan ditetapkan, seperti yang diharapkan), sedangkan a ||= bakan mengevaluasi ke a.

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

    Masih tidak. Pernyataan ini dapat berbeda ketika method_missingada metode yang mengembalikan nilai kebenaran untuk a. Dalam hal ini, a ||= bakan mengevaluasi method_missingpengembalian apa pun , dan tidak berusaha untuk menetapkan a, sedangkan defined?(a) ? (a || a = b) : (a = b)akan ditetapkan auntuk bdan mengevaluasi b.

Oke, oke, jadi apa yang a ||= b setara? Apakah ada cara untuk mengekspresikan ini di Ruby?

Nah, dengan asumsi bahwa saya tidak mengabaikan apa pun, saya percaya a ||= bsecara fungsional setara dengan ... ( drumroll )

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

Tahan! Bukankah itu hanya contoh pertama dengan noop sebelumnya? Ya tidak cukup. Ingat bagaimana saya katakan sebelumnya bahwa a ||= bitu tidak hanya setara dengan a || a = bkapan avariabel lokal tidak ditentukan? Yah, a = nil if falsepastikan itu atidak pernah ditentukan, meskipun garis itu tidak pernah dieksekusi. Variabel lokal di Ruby dibatasi secara leksikal.


Jadi contoh ketiga Anda yang diperluas:(a=b unless a) or a
vol7ron

1
@ vol7ron Itu memiliki masalah yang sama dengan # 2. Jika amerupakan metode, itu akan dipanggil dua kali alih-alih satu kali (jika itu mengembalikan nilai kebenaran pertama kali). Itu dapat menyebabkan perilaku berbeda jika, misalnya, amembutuhkan waktu lama untuk kembali atau memiliki efek samping.
Ajedi32

Juga, kalimat pertama, tidak boleh itu mengatakan assign buntuka , tidak dengan rhs masih menetapkan ke lhs, atau dengan kata lain, tidak dengan lhs masih set nilainya ke rhs?
vol7ron

a ||= bJawaban terbaik yang saya temukan di Internet. Terima kasih.
Eric Duminil

3

unless x x = y end

kecuali x memiliki nilai (bukan nil atau salah), atur sama dengan y

setara dengan

x ||= y


3

Misalkan a = 2danb = 3

LALU, a ||= b akan dihasilkan anilai yaitu2 .

Seperti ketika seorang mengevaluasi ke beberapa nilai yang tidak dihasilkan falseatau nil.. Karena itulah ia lltidak mengevaluasib nilai itu.

Sekarang Misalkan a = nildanb = 3 .

Maka a ||= bakan dihasilkan 3yaitub nilai 's.

Ketika pertama kali mencoba untuk mengevaluasi nilai a yang dihasilkan nil.. jadi dievaluasib nilai.

Contoh terbaik yang digunakan dalam aplikasi ror adalah:

#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

Di mana, User.find_by_id(session[:user_id])dipecat jika dan hanya jika @current_usertidak diinisialisasi sebelumnya.


3

a || = b

Menandakan jika ada nilai yang ada di 'a' dan Anda tidak ingin mengubahnya tetap menggunakan nilai itu, kalau tidak 'a' tidak memiliki nilai, gunakan nilai 'b'.

Kata-kata sederhana, jika sisi kiri jika bukan nol, arahkan ke nilai yang ada, atau arahkan ke nilai di sisi kanan.


2
a ||= b

setara dengan

a || a = b

dan tidak

a = a || b

karena situasi di mana Anda mendefinisikan hash dengan default (hash akan mengembalikan default untuk kunci yang tidak ditentukan)

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

jika Anda menggunakan:

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

a masih:

{}

tetapi ketika Anda menulisnya seperti itu:

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

a menjadi:

{10 => true}

karena Anda telah menetapkan nilai dirinya pada key 10, yang defaultnya menjadi true, jadi sekarang hash didefinisikan untuk key 10, daripada tidak pernah melakukan penugasan sejak awal.


2

Ini seperti contoh malas. Jika variabel sudah ditentukan maka akan mengambil nilai itu alih-alih membuat nilai lagi.


2

Harap juga ingat bahwa ||=ini bukan operasi atom dan karenanya, ini tidak aman untuk thread. Sebagai pedoman praktis, jangan menggunakannya untuk metode kelas.


2

Ini adalah notasi penugasan default

misalnya: x || = 1
ini akan memeriksa untuk melihat apakah x nil atau tidak. Jika x benar-benar nol maka akan menetapkan nilai baru itu (1 dalam contoh kita)

lebih eksplisit:
jika x == nil
x = 1
akhir


salah satu nilatau false, tidak hanyanil
Alex Poca

2

|| = adalah operator penugasan bersyarat

  x ||= y

setara dengan

  x = x || y

atau sebagai alternatif

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

2

Jika XTIDAK memiliki nilai, itu akan diberi nilai Y. Atau, itu akan mempertahankan nilai aslinya, 5 dalam contoh ini:

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

Sebagai kesalahpahaman umum, a ||= btidak sama dengan a = a || b, tetapi berperilaku seperti a || a = b.

Tapi inilah kasus rumit. Jika atidak didefinisikan, a || a = 42naikkan NameError, sambil a ||= 42kembali 42. Jadi, mereka sepertinya bukan ekspresi yang setara.


1

||= memberikan nilai ke kanan hanya jika kiri == nil (atau tidak didefinisikan atau salah).


Anda mungkin berarti 'memberikan nilai ke kiri' bukannya kanan
Maysam Torabi


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

Karena asudah diatur ke1

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

Karena aitunil


Apa tanggal yang dijawab di sini. Mengapa tidak menunjukkan tahun?
Shiv

0
b = 5
a ||= b

Ini diterjemahkan menjadi:

a = a || b

yang mana yang akan

a = nil || 5

akhirnya

a = 5

Sekarang jika Anda memanggil ini lagi:

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

b = 6

Sekarang jika Anda memanggil ini lagi:

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

Jika Anda amati, bnilai tidak akan diberikan a. amasih akan memiliki 5.

Ini adalah Pola Memoisasi yang digunakan di Ruby untuk mempercepat aksesor.

def users
  @users ||= User.all
end

Ini pada dasarnya diterjemahkan menjadi:

@users = @users || User.all

Jadi, Anda akan melakukan panggilan ke database untuk pertama kalinya Anda memanggil metode ini.

Panggilan mendatang ke metode ini hanya akan mengembalikan nilai @usersvariabel instan.


0

||= disebut operator penugasan bersyarat.

Ini pada dasarnya berfungsi sebagai =tetapi dengan pengecualian bahwa jika suatu variabel telah ditetapkan itu tidak akan melakukan apa-apa.

Contoh pertama:

x ||= 10

Contoh kedua:

x = 20
x ||= 10

Dalam contoh pertama xsekarang sama dengan 10. Namun, dalam contoh kedua xsudah didefinisikan sebagai 20. Jadi operator kondisional tidak berpengaruh. xmasih 20 setelah berjalan x ||= 10.


-2

a ||= bsama dengan mengatakan a = b if a.nil?ataua = b unless a

Tetapi apakah ketiga opsi ini menunjukkan kinerja yang sama? Dengan Ruby 2.5.1 ini

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

membutuhkan 0,099 Detik di PC saya, sementara

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

membutuhkan waktu 0,062 Detik. Itu hampir 40% lebih cepat.

dan kemudian kita juga memiliki:

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

yang membutuhkan 0,166 Detik.

Bukannya ini akan membuat dampak kinerja yang signifikan secara umum, tetapi jika Anda memang membutuhkan sedikit optimasi terakhir, maka pertimbangkan hasil ini. Omong-omong: a = 1 unless alebih mudah dibaca untuk pemula, itu cukup jelas.

Catatan 1: alasan untuk mengulangi garis tugas beberapa kali adalah untuk mengurangi overhead loop pada waktu yang diukur.

Catatan 2: Hasilnya serupa jika saya melakukan a=nilnol sebelum setiap tugas.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.