【问题标题】:Why does Proc#binding show a variable is defined when defined? does not为什么 Proc#binding 在定义时显示变量已定义?才不是
【发布时间】:2013-02-07 21:43:38
【问题描述】:

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

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

【问题讨论】:

  • 您使用的 Ruby 的具体版本是什么(包括补丁级别)?我无法在 1.9.3-p374 上复制它。
  • @AndrewMarshall:1.9.3-p362。但是,我可以在 374 和最新的 2.0.0 预览版中复制此行为。 (让我再次强调一下,这只适用于批处理解释器,它不能以交互方式工作,因为解析 REPL 的工作方式不同)
  • 你能定义“批处理解释器”吗?在 IRB、Pry 和 Ruby 中直接运行->(o) { o }.binding.eval('defined? a') 都给我nil
  • @AndrewMarshall 通过ruby file.rb运行它
  • 等等,我可以复制它。我想我知道发生了什么。

标签: ruby metaprogramming


【解决方案1】:

发生的情况是,仅在解析时调用 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? 的评估推迟到整个文件被解析之后。

【讨论】:

  • 是的,defined? 不是方法,而是运算符。 ruby 的另一个问题。
【解决方案2】:

实际上是第六行是谜团的来源。即使代码没有执行,Ruby 也会在条件中创建对象。如果一个变量被赋值,它被隐式声明。 This blog post has a quick explanation

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

【讨论】:

  • 那不是真的。我知道 Ruby 具有 if 行为,而我的问题并没有真正解决这个问题(我所说的仅在 ruby 解释器中发生,irb 会以不同的方式解析)。我删除了if 以澄清!
【解决方案3】:

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

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

【讨论】:

  • 我理解 Ruby 以这种方式解析它,但这仍然不能回答我的问题:如果是这种情况,那么为什么 lambda 的绑定与仅使用在大小 5 和 6 上定义的不匹配。那些两个具有相同的范围(因为绑定只会在创建 lambda 时捕获范围)。我不明白不匹配,你能解释一下吗?
猜你喜欢
  • 1970-01-01
  • 2016-06-07
  • 2011-05-09
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多