| | = (Hoặc bằng) có nghĩa là gì trong Ruby?


340

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:


175

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

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.


2
Liên kết thứ hai đã bị thối bit (nhận xét từ meta bởi stackoverflow.com/users/540162/nightfirecat ).
Andrew Grimm

330
Đó là một câu trả lời rất khó hiểu. Câu trả lời ngắn dường như là: a || = b có nghĩa là, nếu a không xác định thì gán cho nó giá trị của b, nếu không thì để yên. (Ok, có nhiều sắc thái và trường hợp đặc biệt, nhưng đó là trường hợp cơ bản.)
Steve Bennett

20
@SteveBennett: Tôi sẽ không gọi thực tế là 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".
Jörg W Mittag

23
Có thể câu hỏi này đã được hỏi rất nhiều lần vì mọi người cứ trả lời rằng câu hỏi này đã được hỏi rất nhiều lần.
einnatural

8
Với câu trả lời này là dễ dàng để xem tại sao có nhiều chủ đề. Nếu bạn cố gắng tìm kiếm một câu trả lời cho câu hỏi này bằng cách sử dụng một chiếc mũ mới, bạn sẽ nhận thấy rằng tất cả các câu trả lời không rõ ràng. Ví dụ, với cái này bạn chỉ nói những gì không phải. Tôi đề nghị cải thiện câu trả lời của bạn và đưa ra câu trả lời dễ dàng cho người mới: a = b trừ khi a
Arnold Roa

594

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 + 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 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 abcả 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:


52
Cảm ơn bạn cho câu trả lời này, nó có ý nghĩa hơn nhiều.
Tom Hert

chưa tìm kiếm đủ nhưng vẫn không hiểu tại sao bạn lại sử dụng điều này trái ngược với a = a | | b. có lẽ chỉ là ý kiến ​​cá nhân của tôi nhưng hơi nực cười là một sắc thái như vậy tồn tại ...
dtc

2
@dtc, cân nhắc 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.)
antinome

1
Tôi thích câu trả lời khác về cách nó đi sâu, nhưng tôi thích câu trả lời này vì nó đơn giản. Đối với ai đó học Ruby, đây là loại câu trả lời chúng ta cần. Nếu chúng ta biết | | = có nghĩa là gì, thì câu hỏi có lẽ đã được diễn đạt khác nhau.
OBCENEIKON

1
Fyi, 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 || ba ||= 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 aa ||= 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 ...
Ajedi32

32

Câu trả lời ngắn gọn và đầy đủ

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.


1
bạn chắc chứ? Điều này ngụ ý rằng nếu 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 ...)
Steve Bennett

Tôi thấy những gì bạn đang nói. Điều tôi muốn nói là hai dòng tương đương là trạng thái kết thúc sẽ tương đương sau khi toàn bộ dòng được đánh giá, nghĩa là giá trị của a, b và những gì được trả về. Việc phiên dịch viên ruby ​​có sử dụng các trạng thái khác nhau hay không - như một số đánh giá về a - để đạt được điều đó là hoàn toàn có thể. Bất kỳ chuyên gia phiên dịch ruby ​​ngoài kia?
the_minted

3
Điều này không hoàn toàn đú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 ||= ba = 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 ||= ba = a || bkhông.
Ajedi32

1
* hiệu chỉnh: a || a = bsẽ không đánh giá ahai lần khi nào alà đúng.
Ajedi32

1
@the_minted 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.
Ajedi32

27

Tóm lại, a||=bcó nghĩa là: Nếu aundefined, nil or false, gán bcho a. Nếu không, hãy giữ anguyên.


16
Về cơ bản,


x ||= y có nghĩa

nếu xcó bất kỳ giá trị nào, hãy để nó một mình và không thay đổi giá trị, nếu không được đặt xthànhy


13

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.


8
Cái này sai. Vui lòng đọc Ruby-Forum.Com/topic/151660 và các liên kết được cung cấp trong đó.
Jörg W Mittag

1
@Jo (umlaut) rg, tôi không thấy có gì sai về nó. Liên kết của bạn là một danh sách các liên kết khác. Không có lời giải thích thực sự tại sao nó sai, chỉ nghe có vẻ như một đánh giá giá trị của bạn.
đánh trứng

Câu trả lời này là sai, bởi vì nó không chỉ kích hoạt undefined, mà còn trên falsenil, 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
dfherr

Mặc dù bất cứ điều gì không hoàn hảo, câu trả lời này có thể thể hiện (không hoạt động cho nil / false), đây là lần đầu tiên giải thích lý do tại sao bạn muốn sử dụng || =, vì vậy cảm ơn bạn!
Jonathan Tuzman


8

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 ||= ba = 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 ||= bdefined?(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ì 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.


Vì vậy, ví dụ thứ ba mở rộng của bạn:(a=b unless a) or a
vol7ron

1
@ vol7ron Điều đó có một vấn đề tương tự như # 2. Nếu 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ụ.
Ajedi32

Ngoài ra, câu đầu tiên, không nên nói là gán 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?
vol7ron

a ||= bCâu trả lời hay nhất tôi đã tìm thấy trên Internet. Cảm ơn.
Eric Duminil

3

unless x x = y end

trừ khi x có giá trị (không phải là không hoặc sai), hãy đặt nó bằng y

tương đương với

x ||= y


3

Giả sử a = 2b = 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 = nilb = 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.


3

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.


2
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.


2

Nó giống như lười biếng ngay lập tức. Nếu biến đã được xác định, nó sẽ lấy giá trị đó thay vì tạo lại giá trị.


2

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.


2

Đâ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


một trong hai nilhoặc false, không chỉnil
Alex Poca

2

|| =toán tử gán có điều kiện

  x ||= y

tương đương với

  x = x || y

Hay cách khác

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

2

Nếu XKHÔNG có giá trị, nó sẽ được gán giá trị của Y. Khác, nó sẽ bảo tồn giá trị ban đầu của nó, 5 trong ví dụ này:

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

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.


1

||= chỉ gán giá trị cho bên phải nếu left == nil (hoặc không xác định hoặc sai).


bạn có thể có nghĩa là 'gán giá trị cho bên trái' thay vì bên phải
Maysam Torabi


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

Bởi vì ađã được đặt thành1

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

bởi vì anil


Ngày trả lời ở đây là gì Tại sao nó không hiển thị năm?
Shiv

0
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.


0

||= đượ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.


-2

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.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.