25

我们定义一个函数 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似乎不应该存在,因此您不能inspect在外部范围内调用它;在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 提升”只会找到关于 JavaScript 提升的结果。)

4

2 回答 2

62

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

x = 1

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

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

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

扎实的 Rubyist第 6.1.2 章

于 2012-10-17T06:17:28.157 回答
3

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

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

于 2012-10-17T06:08:38.113 回答