为什么我可以引用从未运行的if / unless / case语句之外的变量?

时间:2012-09-27 05:46:09

标签: ruby

为什么以下代码不会抛出错误?

if false
  x = 0
end

x  #=> nil

以下情况会引发错误:

y  # NameError: undefined local variable or method `x' for main:Object

同样的事情发生在unless& case陈述。

3 个答案:

答案 0 :(得分:20)

这是因为Ruby解析器的工作原理。变量由解析器定义,解析器逐行遍历代码,无论它是否实际执行。

解析器看到x =后,它会在当前范围内定义局部变量x(值为nil)。由于if / unless / case / for / while未创建新范围,因此x已在代码块之外定义并可用。并且由于内部块永远不会被评估为条件为假,因此x未分配给(nil}。

这是一个类似的例子:

defined?(x) and x = 0
x  #=> nil

请注意,这是对所发生情况的高级概述,并不一定是解析器的工作方式。

答案 1 :(得分:2)

这与Ruby的范围规则的怪癖有关。

在ruby中,一个未修饰的变量x本身可能是局部变量或方法调用 - 语法无法分辨哪个。由解析器解析局部变量引用,由解析器决定。规则很简单:如果已经在本地范围内看到对同名变量的赋值,则引用是局部变量,并且引用绑定到该局部变量。否则,它是一个方法调用,它将在运行时被查找。

Ruby中的局部变量引用被优化为数组查找(每个局部变量被赋予一个'slot',并且由解析器生成的绑定局部变量引用被转换为槽引用)。使用所有nil初始化数组:

/* initialize local variables */
for (i=0; i < local_size; i++) {
    *sp++ = Qnil;
}

因此,如果您引用尚未分配的局部变量,则通过绑定的本地引用(只有在同一本地范围内的引用之上有跳过的赋值时才会发生),您得到{{1 }}

答案 2 :(得分:1)

我觉得你的问题很有趣所以我试着查一下,发现了这个: I don't understand ruby local scope

正确的答案似乎是Jorg。

让我们看一下当您尝试访问未初始化的变量时会发生什么:

NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object

该异常表明它无法评估变量或方法。它没有抛出相同异常的原因是因为未初始化的局部变量设置为nil。所以puts x是可以的,因为解释器知道x是可变的但未初始化而不是方法。