为什么+ =将其运行的变量的值重置为nil?

Jam*_*ier 3 ruby

以这段代码为例:

class Thing
  attr_accessor :options, :list

  def initialize
    @list    = []
    @options = { published_at_end: 'NOW', published_at_start: 'NOW-2DAYS' }
  end

  def run
    # If you replace this comment with a debugger, the value of list is nil
    list += _some_method(options)
    return list
  end

  private

  def _some_method(options)
    [options[:published_at_start], 1, 2, 3, 4, options[:published_at_end]]
  end
end
Run Code Online (Sandbox Code Playgroud)

如果将其复制/粘贴到irb中,则运行:

  • t = Thing.new
  • t.run

它会输出这个错误:

NoMethodError: undefined method `+' for nil:NilClass
Run Code Online (Sandbox Code Playgroud)

如果删除+=线(只留下return线),它返回[]......所以从我可以告诉,它只是存在+=,设置listnil.我也觉得有趣的是它的值nil在调用之前的行中+=(参见我在代码示例中的注释).

另外,如果使用<<具有flatten相反,你会得到预期的结果:

class Thing
  attr_accessor :options, :list

  def initialize
    @list    = []
    @options = { published_at_end: 'NOW', published_at_start: 'NOW-2DAYS' }
  end

  def run
    list << _some_method(options)
    list.flatten
  end

  private

  def _some_method(options)
    [options[:published_at_start], 1, 2, 3, 4, options[:published_at_end]]
  end
end
Run Code Online (Sandbox Code Playgroud)

如果将其复制/粘贴到irb中,则运行:

  • t = Thing.new
  • t.run

它会输出['NOW-2DAYS', 1, 2, 3, 4, 'NOW'].


为什么+=重置价值listnil?另外,它如何nil+=被调用之前设置其值?

半相关/有用的注意事项 - 由于性能原因,我将使用shovel(<<),但我仍然感兴趣为什么变量被重置为.flattennil

Ste*_*fan 7

list += [1, 2, 3] 相当于:

list = list + [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

因为这是一个赋值,Ruby会创建一个新的局部变量 list,遮蔽你的list方法.从文档:

使用方法分配时,您必须始终拥有接收器.如果您没有接收器,Ruby假定您正在分配一个局部变量

更具体地说,list在解析器遇到时创建局部变量list =.与未初始化的实例变量和全局变量一样,它的值为nil.因此,尝试评估作业的右侧list + [1, 2, 3]是失败的,因为它等同于:

nil + [1, 2, 3]
# NoMethodError: undefined method `+' for nil:NilClass
Run Code Online (Sandbox Code Playgroud)

因此,要获得预期的结果,您必须提供一个明确的接收器:

self.list += [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

或者直接分配给实例变量:

@list += [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

或者使用修改接收器的方法:

list.concat [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)