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

前端 未结 23 2757
情书的邮戳
情书的邮戳 2020-11-21 23:20

What does the following code mean in Ruby?

||=

Does it have any meaning or reason for the syntax?

相关标签:
23条回答
  • 2020-11-21 23:45

    ||= is a conditional assignment operator

      x ||= y
    

    is equivalent to

      x = x || y
    

    or alternatively

    if defined?(x) and x
        x = x
    else 
        x = y
    end
    
    0 讨论(0)
  • 2020-11-21 23:46

    To be precise, a ||= b means "if a is undefined or falsy (false or nil), set a to b and evaluate to (i.e. return) b, otherwise evaluate to a".

    Others often try to illustrate this by saying that a ||= b is equivalent to a || a = b or a = a || b. These equivalencies can be helpful for understanding the concept, but be aware that they are not accurate under all conditions. Allow me to explain:

    • a ||= ba || a = b?

      The behavior of these statements differs when a is an undefined local variable. In that case, a ||= b will set a to b (and evaluate to b), whereas a || a = b will raise NameError: undefined local variable or method 'a' for main:Object.

    • a ||= ba = a || b?

      The equivalency of these statements are often assumed, since a similar equivalence is true for other abbreviated assignment operators (i.e. +=,-=,*=,/=,%=,**=,&=,|=,^=,<<=, and >>=). However, for ||= the behavior of these statements may differ when a= is a method on an object and a is truthy. In that case, a ||= b will do nothing (other than evaluate to a), whereas a = a || b will call a=(a) on a's receiver. As others have pointed out, this can make a difference when calling a=a has side effects, such as adding keys to a hash.

    • a ||= ba = b unless a??

      The behavior of these statements differs only in what they evaluate to when a is truthy. In that case, a = b unless a will evaluate to nil (though a will still not be set, as expected), whereas a ||= b will evaluate to a.

    • a ||= bdefined?(a) ? (a || a = b) : (a = b)????

      Still no. These statements can differ when a method_missing method exists which returns a truthy value for a. In this case, a ||= b will evaluate to whatever method_missing returns, and not attempt to set a, whereas defined?(a) ? (a || a = b) : (a = b) will set a to b and evaluate to b.

    Okay, okay, so what is a ||= b equivalent to? Is there a way to express this in Ruby?

    Well, assuming that I'm not overlooking anything, I believe a ||= b is functionally equivalent to... (drumroll)

    begin
      a = nil if false
      a || a = b
    end
    

    Hold on! Isn't that just the first example with a noop before it? Well, not quite. Remember how I said before that a ||= b is only not equivalent to a || a = b when a is an undefined local variable? Well, a = nil if false ensures that a is never undefined, even though that line is never executed. Local variables in Ruby are lexically scoped.

    0 讨论(0)
  • 2020-11-21 23:46
    a ||= b
    

    is equivalent to

    a || a = b
    

    and not

    a = a || b
    

    because of the situation where you define a hash with a default (the hash will return the default for any undefined keys)

    a = Hash.new(true) #Which is: {}
    

    if you use:

    a[10] ||= 10 #same as a[10] || a[10] = 10
    

    a is still:

    {}
    

    but when you write it like so:

    a[10] = a[10] || 10
    

    a becomes:

    {10 => true}
    

    because you've assigned the value of itself at key 10, which defaults to true, so now the hash is defined for the key 10, rather than never performing the assignment in the first place.

    0 讨论(0)
  • 2020-11-21 23:46

    As a common misconception, a ||= b is not equivalent to a = a || b, 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.

    0 讨论(0)
  • 2020-11-21 23:47

    If X does NOT have a value, it will be assigned the value of Y. Else, it will preserve it's original value, 5 in this example:

    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
    
    0 讨论(0)
提交回复
热议问题