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 a
khô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 ||= true
khô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 ||= b
là một toán tử gán điều kiện . Nó có nghĩa là nếu a
không được xác định hoặc falsey , sau đó đánh giá b
và thiết lập a
kết quả . Tương tự, nếu a
được xác định và đánh giá là trung thực, thì b
khô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 + b
a ||= b
tạm dịch a || a = b
Nó là một tốc ký gần a || a = b
. Sự khác biệt là, khi a
không xác định, a || a = b
sẽ tăng NameError
, trong khi a ||= b
đặt a
thành b
. Sự khác biệt này là không quan trọng nếu a
và b
cả 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] || 2
so với h[1] || h[1] = 2
. Cả hai biểu thức đánh giá 0
như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 = b
tăng một NameError
nếu a
không được xác định. a ||= b
không, nhưng thay vào đó khởi tạo a
và 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 || b
và a ||= b
tôi biết là nếu a=
là một phương thức, nó sẽ được gọi bất kể a
trả về cái gì . Ngoài ra, sự khác biệt duy nhất giữa a = b unless a
và a ||= b
tôi biết là tuyên bố đó được đánh giá nil
thay vì a
nếu a
là 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.
a
sai / 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 end
sẽ ném ra một lỗi nếu a
là undefined, trong khi a ||= b
và a = a || b
sẽ 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á a
gấp đôi khi a
là truthy, trong khi a ||= b
và a = a || b
không.
a || a = b
sẽ không đánh giá a
hai lần khi nào a
là đú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 a
là 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 ||= b
sẽ trả lại 6, nhưng self.a ? self.a : self.a = b
sẽ 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 false
và nil
, có thể không liên quan current_user
, nhưng đặc biệt là false
có 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 ||= b
có nghĩa là "nếu a
không xác định hoặc giả mạo ( false
hoặc nil
), được đặt a
thành b
và đá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 ||= b
tương đương với a || a = b
hoặ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 a
là một biến cục bộ không xác định. Trong trường hợp đó, a ||= b
sẽ đặt a
thành b
(và đánh giá thành b
), trong khi a || a = b
sẽ 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à a
là sự thật. Trong trường hợp đó, a ||= b
sẽ không làm gì cả (trừ đánh giá để a
), trong khi đó a = a || b
sẽ gọi a=(a)
trên a
củ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=a
có 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á a
là khi nào là sự thật. Trong trường hợp đó, a = b unless a
sẽ đánh giá nil
(mặc dù a
vẫn sẽ không được đặt, như mong đợi), trong khi a ||= b
sẽ đá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_missing
phương thức tồn tại trả về giá trị trung thực cho a
. Trong trường hợp này, a ||= b
sẽ đánh giá bất kỳ method_missing
trả về nào và không cố gắng đặt a
, trong khi đó defined?(a) ? (a || a = b) : (a = b)
sẽ được đặt a
thành b
và đá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 ||= b
là 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 ||= b
chỉ không tương đương với a || a = b
khi a
một biến cục bộ không xác định? Chà, a = nil if false
đảm bảo rằng a
khô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
a
là 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ụ, a
mất nhiều thời gian để trở lại hoặc có tác dụng phụ.
b
choa
, 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 ||= b
Câu trả lời hay nhất tôi đã tìm thấy trên Internet. Cảm ơn.
Giả sử a = 2
vàb = 3
THEN, a ||= b
sẽ được dẫn đến a
giá trị tức là 2
.
Như khi một đánh giá đến một số giá trị không dẫn đến false
hoặc nil
.. Đó là lý do tại sao nó ll
không đánh giá giá b
trị của nó.
Bây giờ Giả sử a = nil
và b = 3
.
Sau đó a ||= b
sẽ được dẫn đến giá trị 3
tứ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_user
khô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
nil
hoặc false
, không chỉnil
Là một quan niệm sai lầm phổ biến, a ||= b
khô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 a
không được xác định, a || a = 42
tăng NameError
, trong khi a ||= 42
trả 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, b
giá trị sẽ không được chỉ định a
. a
vẫ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 @users
biế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 x
bâ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. x
vẫn còn 20 sau khi chạy x ||= 10
.
a ||= b
giố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 a
dễ đọ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=nil
không trước mỗi bài tập.