为什么+ =将其操作的变量的值重置为nil?

时间:2016-12-14 14:31:43

标签: ruby

以这段代码为例:

int div = 0;

int res = 3/div;

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

  • 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
  • t = Thing.new

它将输出此错误:

t.run

如果您移除NoMethodError: undefined method `+' for nil:NilClass 行(仅留下+=行),则返回return ...所以从我所知道的,它只是存在将[]设置为+=的{​​{1}}。 我还发现有趣的是,在<{em> list调用之前行中的nil值是nil(请参阅我在代码示例中的评论)。

或者,如果您使用+=代替<<,您将获得预期的结果:

flatten

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

  • 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
  • t = Thing.new

它将输出t.run

为什么['NOW-2DAYS', 1, 2, 3, 4, 'NOW']会将+=的值重置为list? 另外,如何在调用nil之前将其值设置为nil

半相关/有用的旁注 - 由于performance reasons,我将使用铲+=)与<<,但我仍然感兴趣为什么该变量将重置为flatten

2 个答案:

答案 0 :(得分:7)

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

list = list + [1, 2, 3]

因为这是一个assignment,Ruby会创建一个新的局部变量 list,遮蔽你的list方法。来自文档:

  

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

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

nil + [1, 2, 3]
# NoMethodError: undefined method `+' for nil:NilClass

因此,为了得到预期的结果,你必须提供一个明确的接收者:

self.list += [1, 2, 3]

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

@list += [1, 2, 3]

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

list.concat [1, 2, 3]

答案 1 :(得分:1)

我相信list += _some_method(options)正在内部编译为list = list + _some_method(options)。尽管存在list方法,但Ruby将list = :anything解释为定义名为list的新局部变量,从而在方法调用的范围内覆盖list的解释。当Ruby错误地跳到“局部变量”结论时,必须用self.告诉它。这就是切换到self.list += _some_method(options)的原因。

class Thing
    def foo; 'BAR'; end
    def baz; foo = 'ODD'; foo; end
end

Thing.new.foo #=> "BAR" 
Thing.new.baz #=> "ODD" 

但我同意使用list <<后跟flatten(1)仍然更有效。