这是我演示懒惰评估概念的简单程序.
class A
def x
y += 1
end
def y
@y ||= 0
end
end
A.new.x
Run Code Online (Sandbox Code Playgroud)
但是我在运行这个程序时得到了这个结果
NoMethodError:nil的未定义方法`+':NilClass
难道我做错了什么?
什么@y是数组,事情很完美.
class A
def x
y << rand(10)
end
def y
@y ||= []
end
end
a = A.new
a.x
Run Code Online (Sandbox Code Playgroud)
UPDATE
最后我明白了这个问题.Ruby是按值传递的.当我y在第一个例子中调用方法时,Ruby不关心instance_variable,它只复制并返回值@y.
在第二个例子中,Ruby仍然复制并返回@y变量的值.但在这种情况下,@y是真实数组的指针,并且该指针的副本仍然指向同一个数组.
但是为什么错误是undefined method + for nil,我期望y + 1,y等于@y(0)的值.那么为什么nil在这种情况下它会返回y?
了解y += 1真正的作用非常重要.y += 1相当于:
y = y + 1
Run Code Online (Sandbox Code Playgroud)
这意味着你刚刚告诉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
Run Code Online (Sandbox Code Playgroud)
self.y确保您阅读该方法y并且不创建局部变量.此外,您需要一个attr_writer能够调用的setter方法y =.
为什么你的第二个例子开箱即用?因为<<它不是创建局部变量的快捷方式,因此它从中接收数组y.并将<<值移动到该数组中,而无需调用类似的setter方法y =.
有趣的读到这个背景:Ruby的|| =(双管/或等于)真正做到了