0

这个问题专门针对 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)"

让我困惑的是第四行。我不确定为什么这会打印“局部变量”而不是零。这似乎暗示 lambda 在某种程度上“看得更远”。(我defined?认为作为一名操作员与此有关。)

此外,虽然绑定说它已定义,但尝试像这样使用它:

p b.binding.eval "a"

第 6 行的赋值导致 NameError 之前。

编辑:我已经对此进行了测试

  • 1.9.3-362
  • 1.9.3-374
  • 2.0.0-预览2

在所有情况下,我都会得到相同的行为。

4

3 回答 3

2

发生的情况是调用defined?发生在解析时,并且由于a尚不存在变量,因此它nil按预期返回。但是,当您调用时evaldefined?它会延迟到运行时。但是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推迟对 till 的评估有关。defined?

于 2013-02-08T01:23:10.937 回答
0

实际上是第六行是谜团的来源。即使代码没有执行,Ruby 也会在条件中创建对象。如果一个变量被赋值,它被隐式声明。 这篇博文有一个快速的解释

我自己觉得这有点令人惊讶。至于您的代码,在一个新的 IRB 会话中,您应该看到“nil”作为第四行的输出,但是如果您多次运行代码,您的变量已经存在(以 Nil 对象的形式)并且您'会得到一个令人费解的消息,即存在局部变量。YMMV。

于 2013-02-07T22:37:33.500 回答
0

这是 Ruby 源代码解析器的一个特性。如果 Ruby 源代码中确实存在变量赋值,即使在程序执行期间从未调用它,它也会被初始化为 nil(因此它被定义)。第 6 行对局部变量a ( a = 2) 进行了赋值。在第 3 行分配之后定义它并不重要,因为它在源代码执行之前被 ruby​​ 解析器初始化为 nil。由于 lambda 没有引入新的作用域,它知道一个变量,所以你会毫不奇怪地得到“局部变量”结果(a == nil)。有关详细信息,请参阅此答案

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

于 2013-02-08T00:53:46.927 回答