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 a
tidak 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 ||= true
tidak tidak melakukan apa jawaban Anda mengatakan itu melakukan "nuansa".
a ||= b
adalah operator penugasan bersyarat . Ini berarti jika a
tidak terdefinisi atau palsu , maka evaluasi b
dan atur a
hasilnya . Secara setara, jika a
didefinisikan dan dievaluasi untuk kebenaran, maka b
tidak 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 a
tidak ditentukan, a || a = b
akan dinaikkan NameError
, sedangkan a ||= b
set a
ke b
. Perbedaan ini tidak penting jika a
dan b
keduanya 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] || 2
vs h[1] || h[1] = 2
. Kedua ekspresi mengevaluasi 0
tetapi 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 = b
memunculkan NameError
jika a
tidak terdefinisi. a ||= b
tidak, melainkan menginisialisasi a
dan mengaturnya b
. Itulah satu-satunya perbedaan antara keduanya sejauh yang saya tahu. Demikian pula, satu-satunya perbedaan antara a = a || b
dan a ||= b
yang saya sadari adalah bahwa jika a=
metode, itu akan dipanggil terlepas dari apa yang a
kembali. Juga, satu-satunya perbedaan antara a = b unless a
dan a ||= b
yang saya sadari adalah bahwa pernyataan itu dievaluasi nil
daripada a
jika a
itu 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.
a
salah / 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 end
akan melemparkan kesalahan jika a
tidak terdefinisi, sedangkan a ||= b
dan a = a || b
tidak 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 end
mengevaluasi a
dua kali ketika a
adalah truthy, sedangkan a ||= b
dan a = a || b
tidak.
a || a = b
tidak akan mengevaluasi a
dua kali kapan a
benar.
the end state will be equivalent after the whole line has been evaluated
Itu belum tentu benar. Bagaimana jika a
suatu 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 ||= b
akan mengembalikan 6, tetapi self.a ? self.a : self.a = b
akan 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 false
dan nil
, yang mungkin tidak relevan untuk current_user
, tetapi terutama false
dapat tidak diharapkan dalam kasus lain
x ||= y
adalah
x || x = y
"Jika x salah atau tidak terdefinisi, maka x arahkan ke y"
Tepatnya, a ||= b
berarti "jika a
tidak terdefinisi atau palsu ( false
atau nil
), diatur a
ke b
dan dievaluasi ke (yaitu kembali) b
, jika tidak mengevaluasi kea
".
Orang lain sering mencoba menggambarkan ini dengan mengatakan bahwa a ||= b
itu setara dengan a || a = b
atau 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 a
variabel lokal tidak ditentukan. Dalam hal ini, a ||= b
akan diatur a
ke b
(dan dievaluasi ke b
), sedangkan a || a = b
akan 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 a
benar. Dalam hal ini, a ||= b
akan melakukan apa-apa (selain mengevaluasi a
), sedangkan a = a || b
akan memanggil a=(a)
pada a
's penerima. Seperti yang telah ditunjukkan orang lain , ini dapat membuat perbedaan ketika menelepon a=a
memiliki efek samping, seperti menambahkan kunci ke hash.
a ||= b
⇔a = b unless a
??
Perilaku pernyataan ini hanya berbeda dalam apa yang mereka evaluasi ketika a
benar. Dalam hal ini, a = b unless a
akan mengevaluasi ke nil
(meskipun a
masih tidak akan ditetapkan, seperti yang diharapkan), sedangkan a ||= b
akan mengevaluasi ke a
.
a ||= b
⇔ defined?(a) ? (a || a = b) : (a = b)
????
Masih tidak. Pernyataan ini dapat berbeda ketika method_missing
ada metode yang mengembalikan nilai kebenaran untuk a
. Dalam hal ini, a ||= b
akan mengevaluasi method_missing
pengembalian apa pun , dan tidak berusaha untuk menetapkan a
, sedangkan defined?(a) ? (a || a = b) : (a = b)
akan ditetapkan a
untuk b
dan 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 ||= b
secara 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 ||= b
itu tidak hanya setara dengan a || a = b
kapan a
variabel lokal tidak ditentukan? Yah, a = nil if false
pastikan itu a
tidak pernah ditentukan, meskipun garis itu tidak pernah dieksekusi. Variabel lokal di Ruby dibatasi secara leksikal.
(a=b unless a) or a
a
merupakan metode, itu akan dipanggil dua kali alih-alih satu kali (jika itu mengembalikan nilai kebenaran pertama kali). Itu dapat menyebabkan perilaku berbeda jika, misalnya, a
membutuhkan waktu lama untuk kembali atau memiliki efek samping.
b
untuka
, tidak dengan rhs masih menetapkan ke lhs, atau dengan kata lain, tidak dengan lhs masih set nilainya ke rhs?
a ||= b
Jawaban terbaik yang saya temukan di Internet. Terima kasih.
Misalkan a = 2
danb = 3
LALU, a ||= b
akan dihasilkan a
nilai yaitu2
.
Seperti ketika seorang mengevaluasi ke beberapa nilai yang tidak dihasilkan false
atau nil
.. Karena itulah ia ll
tidak mengevaluasib
nilai itu.
Sekarang Misalkan a = nil
danb = 3
.
Maka a ||= b
akan dihasilkan 3
yaitub
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_user
tidak 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
nil
atau false
, tidak hanyanil
Jika X
TIDAK 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, b
nilai tidak akan diberikan a
. a
masih 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 @users
variabel 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 x
sekarang sama dengan 10. Namun, dalam contoh kedua x
sudah didefinisikan sebagai 20. Jadi operator kondisional tidak berpengaruh. x
masih 20 setelah berjalan x ||= 10
.
a ||= b
sama 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 a
lebih 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=nil
nol sebelum setiap tugas.