我的红宝石懒惰评估出了什么问题

时间:2016-06-12 12:18:58

标签: ruby

这是我演示懒惰评估概念的简单程序。

class A
  def x
    y += 1
  end

  def y
    @y ||= 0
  end
end

A.new.x

但是我在运行这个程序时得到了这个结果

  

NoMethodError:nil的未定义方法`+':NilClass

我做错了吗?

@y是数组时,事情就完美了。

class A
  def x
    y << rand(10)
  end

  def y
    @y ||= [] 
  end
end

a = A.new
a.x

更新

最后我理解了这个问题。 Ruby是按值传递的。当我在第一个例子中调用方法y时,Ruby不关心instance_variable,它只复制并返回@y的值。

在第二个示例中,Ruby仍然复制并返回@y变量的值。但在这种情况下,@y是实数组的指针,该指针的副本仍然指向同一个数组。

但为什么错误是undefined method + for nil,我希望y + 1,y等于@y(0)的值。那么为什么在这种情况下它会为y返回nil

1 个答案:

答案 0 :(得分:5)

了解y += 1真正做的事情非常重要。 y += 1相当于:

y = y + 1

这意味着你刚刚告诉Ruby你想将y + 1分配给局部变量y。因为Ruby更喜欢从调用方法读取局部变量,所以它使用局部变量y的当前值并尝试添加1。但此时局部变量y仍为nil,因此此操作会引发undefined method '+' for nil:NilClass异常。

我猜您希望y += 1调用方法y,添加1并将结果写回@y。要实现这一点,您必须将代码更改为:

class A
  attr_writer :y

  def x
    self.y += 1
  end

  def y
    @y ||= 0
  end
end

a = A.new
a.x
#=> 1
a.x
#=> 2

self.y确保您阅读方法y并且不创建局部变量。此外,您需要一个setter方法attr_writer才能调用y =

为什么你的第二个例子开箱即用?由于<<不是创建局部变量的快捷方式,因此它从y接收数组。并且<<将值移动到该数组中,而无需调用y =之类的setter方法。

有趣的阅读在此背景下:What Ruby’s ||= (Double Pipe / Or Equals) Really Does