为何定义Proc#binding显示变量?才不是

时间:2013-02-07 21:43:39

标签: ruby metaprogramming

这个问题具体涉及Ruby 1.9.3:

p defined?(a)
p binding.eval "defined?(a)"
b = lambda { |x| x }
p b.binding.eval "defined?(a)" # This prints "local-variable"
p defined?(a) # This prints nil!
a = 2
p defined?(a)
p b.binding.eval "defined?(a)"

令我困惑的是第四行。我不确定为什么打印“本地变量”而不是nil。这似乎意味着lambda在某种程度上“看得更远”。 (我认为作为运营商的defined?与此有关。)

此外,尽管绑定表明已定义,但尝试使用它如下:

p b.binding.eval "a"
在之前

第6行的赋值会导致NameError。

编辑: 我在

上测试了这个
  • 1.9.3-362
  • 1.9.3-374
  • 2.0.0-preview2

我在所有情况下都有相同的行为。

3 个答案:

答案 0 :(得分:2)

只是在解析时调用defined?,并且由于尚未存在变量a,它会按预期返回nil。但是,当您eval调用defined?时,它会延迟到运行时。但是defined?仍然是词法范围的,并且由于整个文件已经被解析和编译,变量a 确实存在,因为它是在分析时创建的,现在已经完成了。

我们可以用简化的例子来说明这一点:

defined? a         #=> nil
eval 'defined? a'  #=> "local-variable"
a = 2
defined? a         #=> "local-variable"

但是如果我们根本没有定义a

defined? a         #=> nil
eval 'defined? a'  #=> nil

正如您所看到的,它与binding没有任何关系,而只是与eval推迟评估defined?直到整个文件之后已被解析。

答案 1 :(得分:0)

实际上,第六行是神秘的源头。即使代码未执行,Ruby也会在条件语句中创建对象。如果指定了变量,则隐式声明它。 This blog post has a quick explanation

我发现这有点令人惊讶。至于你的代码,在一个新的IRB会话中,你应该看到'nil'作为第四行的输出,但如果你多次运行代码,你的变量已经存在(以Nil对象的形式)而你我会得到一个局部变量存在的令人困惑的消息。 YMMV。

答案 2 :(得分:0)

这是Ruby源解析器的一个特性。如果在Ruby源代码中确实存在变量赋值,那么即使在程序执行期间从未调用过,它也会被初始化为nil(因此它被定义)。 第6行的赋值是局部变量 a a = 2)。无论是在第3行之后定义,因为它甚至在执行源之前被ruby解析器初始化为nil。由于lambda没有引入新的范围,它知道一个一个变量,所以你得到了“本地变量”结果(a == nil)。 有关详细信息,请参阅此answer

在irb中运行或传递给ruby解释器之间的不同行为与上一段​​直接相关。 irb逐行计算表达式(REPL),ruby在执行之前解析整个源。