What does ||= (or-equals) mean in Ruby?

2018-12-31 03:04发布

What does the following code mean in Ruby?

||=

Does it have any meaning or reason for the syntax?

20条回答
与风俱净
2楼-- · 2018-12-31 03:34
x ||= y

is

x || x = y

"if x is false or undefined, then x point to y"

查看更多
其实,你不懂
3楼-- · 2018-12-31 03:34

as a common misconception a||=b is not equivalent to a = a||b but it is but it behaves like a || a = b

But here comes a tricky case

If a is not defined, a || a = 42 raises NameError, while a ||= 42 returns 42. So, they don't seem to be equivalent expressions.

查看更多
梦该遗忘
4楼-- · 2018-12-31 03:35

unless x x = y end

unless x has a value (it's not nil or false), set it equal to y

is equivalent to

x ||= y

查看更多
墨雨无痕
5楼-- · 2018-12-31 03:35

Please also remember that ||= isn't an atomic operation and so, it isn't thread safe. As rule of thumb, don't use it for class methods.

查看更多
与风俱净
6楼-- · 2018-12-31 03:36

a ||= b is the same as saying a = b if a.nil? or a = b unless a

But do all 3 options show the same performance? With Ruby 2.5.1 this

1000000.times do
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
  a ||= 1
end

takes 0.099 Seconds on my PC, while

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

takes 0.062 Seconds. That's almost 40% faster.

and then we also have:

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

which takes 0.166 Seconds.

Not that this will make a significant performance impact in general, but if you do need that last bit of optimization, then consider this result. By the way: a = 1 unless a is easier to read for the novice, it is self-explanatory.

Note 1: reason for repeating the assignment line multiple times is to reduce the overhead of the loop on the time measured.

Note 2: The results are similar if I do a=nil nil before each assignment.

查看更多
谁念西风独自凉
7楼-- · 2018-12-31 03:37

a ||= b is a "conditional assignment operator". It is sort-of-but-not-quite(*) shorthand for a || a = b.

It means "if a is undefined or falsey (false or nil), then evaluate b and set a to the result".

For example:

> a ||= nil
=> nil
> a ||= 0;
=> 0
> a ||= 2;
=> 0

> foo = false;
=> false
> foo ||= true;
=> true
> foo ||= false;
=> true

Ruby's short circuit evaluation means that if a is defined and evaluates to truthy, then the right hand side of the operator is not evaluated, and no assignment takes place. This distinction is unimportant if a and b are both local variables, but is significant if either is a getter/setter method of a class.

Confusingly, it looks similar to other assignment operators (such as +=) but behaves differently.

a += b   translates to   a = a + b

a ||= b   roughly translates to*    a || a = b

*Except that, when a is undefined, a || a = b would be NameError, whereas a ||= b sets a to b.

Further reading:

查看更多
登录 后发表回答