这是我演示懒惰评估概念的简单程序。
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
?
答案 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