为什么Ruby似乎从case语句中提升变量声明,即使该代码路径没有被执行?

我们定义一个函数foo:

def foo(s) case s when'foo' x = 3 puts x.inspect when 'bar' y = 4 puts y.inspect end puts x.inspect puts y.inspect end 

然后我们将其称为:

 1.9.3p194 :017 > foo('foo') in foo scope 3 in outer scope 3 nil => nil 1.9.3p194 :018 > foo('bar') in bar scope 3 in outer scope nil 3 => nil 

为什么在任何一种情况下函数都不会抛出关于未注册的局部变量的错误? 在第一种情况下,变量y似乎不应该存在,因此你不能在外部范围内调用它; 在第二种情况下对于x也是如此。

这是另一个类似的例子:

 def test1 x = 5 if false puts x.inspect end def test2 puts x.inspect end 

然后:

 1.9.3p194 :028 > test1 nil => nil 1.9.3p194 :029 > test2 NameError: undefined local variable or method `x' for main:Object 

这里发生了什么? 看起来Ruby正在将变量声明提升到外部范围,但我不知道这是Ruby所做的事情。 (搜索“ruby hoisting”只会显示有关JavaScript吊装的结果。)

当Ruby解析器看到序列标识符时,等号,值,如此表达式中所示

 x = 1 

它为名为x的局部变量分配空间。 变量的创建 – 不是赋值给它,而是变量的内部创建 – 总是作为这种表达式的结果发生,即使代码没有被执行! 考虑这个例子:

 if false x = 1 end px # Output: nil py # Fatal Error: y is unknown 

不执行对x的赋值,因为它包含在失败的条件测试中。 但Ruby解析器看到序列x = 1 ,从中推断出程序涉及局部变量x 。 解析器不关心是否为x分配了值。 它的工作只是为了需要分配空间的本地变量的代码。 结果是x栖息在一种奇怪的变量中。 它已经被生成并初始化nil 。 在这方面,它与一个根本不存在的变量不同; 正如您在示例中所看到的,检查x会给出值nil ,而尝试检查不存在的变量y导致致命错误。 但是虽然x存在,但它在该计划中没有发挥任何作用。 它仅作为解析过程的工件存在。

扎实的Rubyist第6.1.2章

ruby解析器遍历每一行并设置为nil all variable = 。 实际执行或未执行的代码无关紧要。

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