为什么我可以引用从未运行的if / unless / case语句之外的变量?
为什么以下代码不会抛出错误?
if false x = 0 end x #=> nil
以下确实会引发错误:
y # NameError: undefined local variable or method `x' for main:Object
unless
& case
语句也会发生同样的事情。
这是因为Ruby解析器的工作原理。 变量由解析器定义,解析器逐行遍历代码,无论它是否实际执行。
一旦解析器看到x =
,它就定义了当前范围内的局部变量x
(值为nil
)。 由于if
/ unless
/ case
/ for
/ while
不创建新范围,因此x
在代码块之外定义并可用。 并且由于内部块永远不会被评估为条件为假,因此x
未被赋值(因此nil
)。
这是一个类似的例子:
defined?(x) and x = 0 x #=> nil
请注意,这是对所发生情况的相当高级别的概述,并不一定完全是解析器的工作方式。
这与Ruby的范围规则的怪癖有关。
在ruby中,一个未修饰的变量x
本身可能是局部变量或方法调用 – 语法无法分辨哪个。 由解析器解析局部变量引用,由解析器决定。 规则很简单:如果已在本地范围内看到对同名变量的赋值,则引用是局部变量,并且引用绑定到该局部变量。 否则,它是一个方法调用,它将在运行时被查找。
Ruby中的局部变量引用被优化为数组查找(每个局部变量被赋予一个’slot’,并且由解析器生成的绑定局部变量引用被转换为槽引用)。 使用所有nil
初始化数组:
/* initialize local variables */ for (i=0; i < local_size; i++) { *sp++ = Qnil; }
因此,如果您引用尚未分配的局部变量,则通过绑定的本地引用(只有在同一本地范围内的引用之上有跳过的赋值时才会发生),您将得到nil
。
我认为你的问题很有趣,所以我试着查一下,发现了这个: 我不明白ruby的局部范围
正确的答案似乎是Jorg。
让我们看一下当您尝试访问未初始化的变量时会发生什么:
NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object
该exception表明它无法评估变量或方法。 它不抛出相同exception的原因是因为未初始化的局部变量设置为nil。 所以puts x
是可以的,因为解释器知道x
是可变的但未初始化而不是方法。