Mã sau có ý nghĩa gì trong Ruby?
||=
Nó có bất kỳ ý nghĩa hoặc lý do cho cú pháp?
Mã sau có ý nghĩa gì trong Ruby?
||=
Nó có bất kỳ ý nghĩa hoặc lý do cho cú pháp?
Câu trả lời:
Câu hỏi này đã được thảo luận rất thường xuyên trên các danh sách gửi thư của Ruby và các blog của Ruby mà giờ đây thậm chí còn có các chủ đề trong danh sách gửi thư của Ruby với mục đích duy nhất là thu thập các liên kết đến tất cả các chủ đề khác trong danh sách gửi thư của Ruby thảo luận về vấn đề này .
Đây là một: Danh sách chính xác của các chủ đề và trang | | = (HOẶC bằng)
Nếu bạn thực sự muốn biết chuyện gì đang xảy ra, hãy xem Phần 11.4.2.3 "Bài tập viết tắt" của Đặc tả Dự thảo Ngôn ngữ Ruby .
Là một xấp xỉ đầu tiên,
a ||= b
tương đương với
a || a = b
và không tương đương với
a = a || b
Tuy nhiên, đó chỉ là một xấp xỉ đầu tiên, đặc biệt nếu akhông được xác định. Các ngữ nghĩa cũng khác nhau tùy thuộc vào việc nó là một phép gán biến đơn giản, phép gán phương thức hoặc phép gán chỉ mục:
a ||= b
a.c ||= b
a[c] ||= b
Tất cả đều được đối xử khác nhau.
a = false; a ||= truekhông phải làm những gì câu trả lời của bạn nói nó một "sắc thái".
a ||= blà một toán tử gán điều kiện . Nó có nghĩa là nếu akhông được xác định hoặc falsey , sau đó đánh giá bvà thiết lập akết quả . Tương tự, nếu ađược xác định và đánh giá là trung thực, thì bkhông được đánh giá và không có sự phân công nào diễn ra. Ví dụ:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Một cách khó hiểu, nó trông tương tự như các toán tử gán khác (chẳng hạn như +=), nhưng hoạt động khác nhau.
a += b Dịch sang a = a + ba ||= b tạm dịch a || a = bNó là một tốc ký gần a || a = b. Sự khác biệt là, khi akhông xác định, a || a = bsẽ tăng NameError, trong khi a ||= bđặt athành b. Sự khác biệt này là không quan trọng nếu avà bcả hai biến cục bộ, nhưng rất quan trọng nếu một trong hai phương thức getter / setter của một lớp.
Đọc thêm:
h = Hash.new(0); h[1] ||= 2. Bây giờ hãy xem xét hai mở rộng có thể h[1] = h[1] || 2so với h[1] || h[1] = 2. Cả hai biểu thức đánh giá 0nhưng đầu tiên không cần thiết làm tăng kích thước của hàm băm. Có lẽ đó là lý do Matz chọn ||=cách hành xử giống như bản mở rộng thứ hai. (Tôi dựa trên ví dụ này từ một trong những chủ đề được liên kết đến trong một câu trả lời khác.)
a || a = btăng một NameErrornếu akhông được xác định. a ||= bkhông, nhưng thay vào đó khởi tạo avà thiết lập nó b. Đó là điểm khác biệt duy nhất giữa hai người theo như tôi biết. Tương tự, sự khác biệt duy nhất giữa a = a || bvà a ||= btôi biết là nếu a=là một phương thức, nó sẽ được gọi bất kể atrả về cái gì . Ngoài ra, sự khác biệt duy nhất giữa a = b unless avà a ||= btôi biết là tuyên bố đó được đánh giá nilthay vì anếu alà sự thật. Rất nhiều xấp xỉ, nhưng không có gì tương đương ...
a ||= b
đánh giá theo cùng một cách với từng dòng sau
a || a = b
a ? a : a = b
if a then a else a = b end
-
Mặt khác,
a = a || b
đánh giá theo cùng một cách với từng dòng sau
a = a ? a : b
if a then a = a else a = b end
-
Chỉnh sửa: Như AJedi32 đã chỉ ra trong các nhận xét, điều này chỉ đúng nếu: 1. a là một biến xác định. 2. Đánh giá một lần và hai lần không dẫn đến sự khác biệt về trạng thái chương trình hoặc hệ thống.
asai / không / không xác định, nó được đánh giá hai lần. (Nhưng tôi không biết Ruby, vì vậy tôi không biết liệu giá trị có thể được 'đánh giá' chính xác không ...)
a || a = b, a ? a : a = b, if a then a else a = b end, Và if a then a = a else a = b endsẽ ném ra một lỗi nếu alà undefined, trong khi a ||= bvà a = a || bsẽ không. Ngoài ra, a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : b, và if a then a = a else a = b endđánh giá agấp đôi khi alà truthy, trong khi a ||= bvà a = a || bkhông.
a || a = bsẽ không đánh giá ahai lần khi nào alà đúng.
the end state will be equivalent after the whole line has been evaluatedĐiều đó không hẳn đúng. Điều gì nếu alà một phương pháp? Phương pháp có thể có tác dụng phụ. Ví dụ với public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5, self.a ||= bsẽ trả lại 6, nhưng self.a ? self.a : self.a = bsẽ trở lại 7.
Nó có nghĩa là hoặc bằng với. Nó kiểm tra xem giá trị bên trái có được xác định không, sau đó sử dụng giá trị đó. Nếu không, sử dụng giá trị bên phải. Bạn có thể sử dụng nó trong Rails để lưu trữ các biến đối tượng trong các mô hình.
Một ví dụ dựa trên Rails nhanh, trong đó chúng tôi tạo một hàm để tìm nạp người dùng hiện đang đăng nhập:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
Nó kiểm tra xem biến đối tượng @cien_user có được đặt không. Nếu có, nó sẽ trả về nó, do đó lưu một cuộc gọi cơ sở dữ liệu. Tuy nhiên, nếu nó không được đặt, chúng tôi thực hiện cuộc gọi và sau đó đặt biến @cien_user thành đó. Đây là một kỹ thuật bộ nhớ đệm thực sự đơn giản nhưng rất phù hợp khi bạn tìm nạp cùng một biến đối tượng trên ứng dụng nhiều lần.
undefined, mà còn trên falsevà nil, có thể không liên quan current_user, nhưng đặc biệt là falsecó thể không được khai thác trong các trường hợp khác
x ||= y
Là
x || x = y
"nếu x sai hoặc không xác định, thì x trỏ đến y"
Nói chính xác, a ||= bcó nghĩa là "nếu akhông xác định hoặc giả mạo ( falsehoặc nil), được đặt athành bvà đánh giá thành (tức là trả về) b, nếu không thì đánh giá thành a".
Những người khác thường cố gắng minh họa điều này bằng cách nói rằng a ||= btương đương với a || a = bhoặc a = a || b. Những tương đương này có thể hữu ích để hiểu khái niệm, nhưng lưu ý rằng chúng không chính xác trong mọi điều kiện. Cho phép tôi giải thích:
a ||= bĐượca || a = b không?
Hành vi của các câu lệnh này khác nhau khi alà một biến cục bộ không xác định. Trong trường hợp đó, a ||= bsẽ đặt athành b(và đánh giá thành b), trong khi a || a = bsẽ tăng NameError: undefined local variable or method 'a' for main:Object.
a ||= bĐượca = a || b không?
Các tương đương của các báo cáo này thường được giả định, vì một tương đương tương tự cũng đúng cho khác phân công viết tắt nhà khai thác (ví dụ +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, và >>=). Tuy nhiên, đối ||=với hành vi của các tuyên bố này có thể khác nhau khi a=là một phương thức trên một đối tượng và alà sự thật. Trong trường hợp đó, a ||= bsẽ không làm gì cả (trừ đánh giá để a), trong khi đó a = a || bsẽ gọi a=(a)trên acủa người nhận. Như những người khác đã chỉ ra, điều này có thể tạo ra sự khác biệt khi gọi a=acó tác dụng phụ, chẳng hạn như thêm khóa vào hàm băm.
a ||= b⇔a = b unless a ??
Hành vi của những tuyên bố này chỉ khác nhau ở những gì họ đánh giá alà khi nào là sự thật. Trong trường hợp đó, a = b unless asẽ đánh giá nil(mặc dù avẫn sẽ không được đặt, như mong đợi), trong khi a ||= bsẽ đánh giá a.
a ||= b⇔defined?(a) ? (a || a = b) : (a = b) ????
Vẫn không có. Các câu lệnh này có thể khác nhau khi một method_missingphương thức tồn tại trả về giá trị trung thực cho a. Trong trường hợp này, a ||= bsẽ đánh giá bất kỳ method_missingtrả về nào và không cố gắng đặt a, trong khi đó defined?(a) ? (a || a = b) : (a = b)sẽ được đặt athành bvà đánh giá thành b.
Được rồi, được rồi, vì vậy những gì là a ||= b tương đương với? Có cách nào để thể hiện điều này trong Ruby?
Chà, giả sử rằng tôi không nhìn thấy gì cả, tôi tin a ||= blà có chức năng tương đương với ... ( trống )
begin
a = nil if false
a || a = b
end
Giữ lấy! Không phải đó chỉ là ví dụ đầu tiên với một noop trước nó sao? Vâng, không hoàn toàn. Hãy nhớ làm thế nào tôi đã nói trước đó a ||= bchỉ không tương đương với a || a = bkhi amột biến cục bộ không xác định? Chà, a = nil if falseđảm bảo rằng akhông bao giờ được xác định, mặc dù dòng đó không bao giờ được thực thi. Các biến cục bộ trong Ruby nằm trong phạm vi từ vựng.
(a=b unless a) or a
alà một phương thức, nó sẽ được gọi hai lần thay vì một lần (nếu nó trả về giá trị trung thực lần đầu tiên). Điều đó có thể khiến các hành vi khác nhau nếu, ví dụ, amất nhiều thời gian để trở lại hoặc có tác dụng phụ.
bchoa , không phải rhs vẫn gán cho lhs, hay nói cách khác, không phải lhs vẫn đặt giá trị của nó cho rhs sao?
a ||= bCâu trả lời hay nhất tôi đã tìm thấy trên Internet. Cảm ơn.
Giả sử a = 2vàb = 3
THEN, a ||= b sẽ được dẫn đến agiá trị tức là 2.
Như khi một đánh giá đến một số giá trị không dẫn đến falsehoặc nil.. Đó là lý do tại sao nó llkhông đánh giá giá btrị của nó.
Bây giờ Giả sử a = nilvà b = 3.
Sau đó a ||= bsẽ được dẫn đến giá trị 3tức là b.
Vì lần đầu tiên cố gắng đánh giá giá trị của một kết quả dẫn đến nil.. vì vậy nó đã đánh giáb trị của nó.
Ví dụ tốt nhất được sử dụng trong ứng dụng ror là:
#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
Trường hợp, User.find_by_id(session[:user_id])được bắn nếu và chỉ khi @current_userkhông được khởi tạo trước.
a | | = b
Biểu thị nếu có bất kỳ giá trị nào xuất hiện trong 'a' và bạn không muốn thay đổi giá trị đó bằng cách sử dụng giá trị đó, nếu không 'a' không có bất kỳ giá trị nào, hãy sử dụng giá trị của 'b'.
Các từ đơn giản, nếu phía bên trái nếu không null, trỏ đến giá trị hiện có, khác chỉ đến giá trị ở phía bên phải.
a ||= b
tương đương với
a || a = b
và không
a = a || b
do tình huống trong đó bạn xác định hàm băm với mặc định (hàm băm sẽ trả về mặc định cho bất kỳ khóa không xác định nào)
a = Hash.new(true) #Which is: {}
nếu bạn dùng:
a[10] ||= 10 #same as a[10] || a[10] = 10
vẫn là:
{}
nhưng khi bạn viết nó như vậy:
a[10] = a[10] || 10
a trở thành:
{10 => true}
bởi vì bạn đã gán giá trị của chính nó tại khóa 10, mặc định là true, nên bây giờ hàm băm được xác định cho khóa 10, thay vì không bao giờ thực hiện phép gán ở vị trí đầu tiên.
Cũng xin nhớ rằng đó ||=không phải là một hoạt động nguyên tử và vì vậy, nó không phải là chủ đề an toàn. Theo nguyên tắc thông thường, không sử dụng nó cho các phương thức lớp.
Đây là ký hiệu gán mặc định
ví dụ: x || = 1
cái này sẽ kiểm tra xem x có phải là con số không hay không. Nếu x thực sự là không thì nó sẽ gán cho nó giá trị mới (1 trong ví dụ của chúng tôi)
rõ ràng hơn:
nếu x == nil
x = 1
kết thúc
nilhoặc false, không chỉnil
Là một quan niệm sai lầm phổ biến, a ||= bkhông tương đương a = a || b, nhưng nó hành xử như thế nào a || a = b.
Nhưng ở đây có một trường hợp khó khăn. Nếu akhông được xác định, a || a = 42tăng NameError, trong khi a ||= 42trả lại 42. Vì vậy, chúng dường như không phải là biểu thức tương đương.
||= chỉ gán giá trị cho bên phải nếu left == nil (hoặc không xác định hoặc sai).
Cú pháp ruby-lang này. Câu trả lời đúng là kiểm tra tài liệu ruby-lang. Tất cả các giải thích khác làm xáo trộn .
"ruby-lang docs Viết tắt".
https://docs.ruby-lang.org/en/2.4.0/syntax/assocate_rdoc.html#label- Abenameviated+Assocation
b = 5
a ||= b
Điều này dịch là:
a = a || b
đó sẽ là
a = nil || 5
cuối cùng thì
a = 5
Bây giờ nếu bạn gọi lại cái này:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Bây giờ nếu bạn gọi lại cái này:
a ||= b
a = a || b
a = 5 || 6
a = 5
Nếu bạn quan sát, bgiá trị sẽ không được chỉ định a. avẫn sẽ có 5.
Đây là Mẫu Ghi nhớ đang được sử dụng trong Ruby để tăng tốc độ truy cập.
def users
@users ||= User.all
end
Điều này về cơ bản dịch là:
@users = @users || User.all
Vì vậy, bạn sẽ thực hiện cuộc gọi đến cơ sở dữ liệu lần đầu tiên bạn gọi phương thức này.
Các cuộc gọi trong tương lai của phương thức này sẽ chỉ trả về giá trị của @usersbiến thể hiện.
||= được gọi là toán tử gán điều kiện.
Về cơ bản nó hoạt động như =nhưng với một ngoại lệ là nếu một biến đã được gán thì nó sẽ không làm gì cả.
Ví dụ đầu tiên:
x ||= 10
Ví dụ thứ hai:
x = 20
x ||= 10
Trong ví dụ đầu tiên xbây giờ bằng 10. Tuy nhiên, trong ví dụ thứ hai xđã được định nghĩa là 20. Vì vậy, toán tử điều kiện không có hiệu lực. xvẫn còn 20 sau khi chạy x ||= 10.
a ||= bgiống như nói a = b if a.nil?haya = b unless a
Nhưng liệu cả 3 tùy chọn đều cho thấy hiệu suất như nhau? Với Ruby 2.5.1 này
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
mất 0,099 giây trên PC của tôi, trong khi
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
mất 0,062 giây. Điều đó nhanh hơn gần 40%.
và sau đó chúng ta cũng có:
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
trong đó mất 0.166 giây.
Không phải điều này sẽ tạo ra một tác động hiệu suất đáng kể nói chung, nhưng nếu bạn cần chút tối ưu hóa cuối cùng đó, thì hãy xem xét kết quả này. Nhân tiện: a = 1 unless adễ đọc hơn cho người mới, nó là tự giải thích.
Lưu ý 1: lý do để lặp lại dòng gán nhiều lần là để giảm chi phí của vòng lặp trên thời gian đo.
Lưu ý 2: Kết quả tương tự nếu tôi làm a=nilkhông trước mỗi bài tập.