Apa arti kode berikut di Ruby?
||=
Apakah ada arti atau alasan untuk sintaksis?
Apa arti kode berikut di Ruby?
||=
Apakah ada arti atau alasan untuk sintaksis?
Jawaban:
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.
a = false; a ||= truetidak tidak melakukan apa jawaban Anda mengatakan itu melakukan "nuansa".
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 + ba ||= b diterjemahkan secara kasar menjadi a || a = bIni 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:
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.)
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 ...
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.
asalah / nol / tidak terdefinisi, itu dievaluasi dua kali. (Tapi saya tidak tahu Ruby, jadi saya tidak tahu apakah nilai-nilai dapat 'dievaluasi' persis ...)
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.
a || a = btidak akan mengevaluasi adua kali kapan abenar.
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.
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.
undefined, tetapi juga pada falsedan nil, yang mungkin tidak relevan untuk current_user, tetapi terutama falsedapat tidak diharapkan dalam kasus lain
x ||= y
adalah
x || x = y
"Jika x salah atau tidak terdefinisi, maka x arahkan ke y"
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 ||= b ⇔ a || 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 ||= b ⇔ a = 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 ||= b⇔a = 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 ||= b ⇔ defined?(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.
(a=b unless a) or a
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.
buntuka , tidak dengan rhs masih menetapkan ke lhs, atau dengan kata lain, tidak dengan lhs masih set nilainya ke rhs?
a ||= bJawaban terbaik yang saya temukan di Internet. Terima kasih.
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.
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.
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.
Harap juga ingat bahwa ||=ini bukan operasi atom dan karenanya, ini tidak aman untuk thread. Sebagai pedoman praktis, jangan menggunakannya untuk metode kelas.
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
nilatau false, tidak hanyanil
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
||= memberikan nilai ke kanan hanya jika kiri == nil (atau tidak didefinisikan atau salah).
Sintaks ruby-lang ini. Jawaban yang benar adalah dengan memeriksa dokumentasi ruby-lang. Semua penjelasan lainnya dikaburkan .
"ruby-lang docs Assignment Disingkat".
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
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.
||= 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.
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.