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

cli*_*zin 25 ruby

我们定义一个函数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
Run Code Online (Sandbox Code Playgroud)

然后我们将其称为如下:

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 
Run Code Online (Sandbox Code Playgroud)

为什么函数不会在任何一种情况下抛出关于未注册的局部变量的错误?在第一种情况下,变量y似乎不应该存在,所以你不能inspect在外部范围内调用它; 同为x在第二种情况下.

这是另一个类似的例子:

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

def test2
  puts x.inspect
end
Run Code Online (Sandbox Code Playgroud)

然后:

1.9.3p194 :028 > test1
nil
 => nil 

1.9.3p194 :029 > test2
NameError: undefined local variable or method `x' for main:Object
Run Code Online (Sandbox Code Playgroud)

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

ck3*_*k3g 60

当Ruby解析器看到序列标识符时,等号,值,就像在这个表达式中一样

x = 1
Run Code Online (Sandbox Code Playgroud)

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

if false
  x = 1
end
p x # Output: nil
p y # Fatal Error: y is unknown
Run Code Online (Sandbox Code Playgroud)

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

有根据的Rubyist第6.1.2章