未定义的本地变量基于Ruby中的语法

在以下Ruby代码中,

#! /usr/bin/env ruby
x = true
y = x and z = y
puts "z: #{z}"

如预期的那样,它将输出z: true

但在下面的一个中,我期望它有相同的行为:

#! /usr/bin/env ruby
x = true
z = y if y = x
puts "z: #{z}"

它导致

未定义的局部变量或方法'y'for main:Object(NameError)

这是为什么?

我明白我正在做一个任务,并隐式检查赋值,以确定是否运行z = y 。 我也明白,如果我在x = 5行之后添加y, y = nil声明,它将按预期方式通过并运行。

但是,期望语言应该首先评估if部分,然后评估其内容,并且第二部分代码的行为与第一部分代码的行为相同吗?


TL; DR

这实际上是解释者特定的。 这个问题出现在MRI Ruby 2.1.2和JRuby 1.7.13中,但在Rubinius中按预期工作。 例如,Rubinius 2.2.10:

x = true
z = y if y = x
#=> true

在MRI中,使用Ripper进行的一些探索表明,尽管AST分配类似,Ruby仍然以不同的方式处理后置条件。 它实际上在构建AST时使用不同的标记作为后置条件,并且这似乎对赋值表达式的评估顺序有影响。 对于Ruby核心团队来说,这是否应该是这种情况,还是可以修复的问题。

为什么它与逻辑和

x = true
y = x and z = y

这是成功的,因为它实际上是按顺序分配的两个分配,因为true分配给了x,因此评估为truthy。 由于第一个表达式是truthy,下一个由逻辑连接的表达式也被评估,并且同样评估为truthy。

y = x
#=> true

z = y
#=> true

换句话说,x被赋值为true ,然后z也被赋值为true 。 任何一个任务的右侧都没有定义。

为什么它失败后条件

x = true
z = y if y = x

在这种情况下,实际上首先评估后置条件。 您可以通过查看AST来看到这一点:

require 'pp'
require 'ripper'

x = true

pp Ripper.sexp 'z = y if y = x'
[:program,
 [[:if_mod,
   [:assign,
    [:var_field, [:@ident, "y", [1, 9]]],
    [:vcall, [:@ident, "x", [1, 13]]]],
   [:assign,
    [:var_field, [:@ident, "z", [1, 0]]],
    [:vcall, [:@ident, "y", [1, 4]]]]]]]

与第一个例子不同的是,y在第一个表达式中被赋予了true ,因此在赋给z之前在第二个表达式中解析为true ,在这种情况下,y被评估,但仍未定义。 这引发了一个NameError。

当然,我们可以合理地争辩说这两个表达式都包含赋值,如果Ruby的解析器首先评估y = x ,那么y就不会是未定义的,就像它与正常的if语句一样(请参阅下面的AST)。 这可能只是后置条件的一个怪癖,如果语句和Ruby处理if_mod标记的方式。

成功与:if而不是:if_mod令牌

如果你反转逻辑并使用正常的if语句,它可以正常工作:

x = true
if y = x
  z = y
end
#=> true

看着开膛手产生下面的AST:

require 'pp'
require 'ripper'

x = true

pp Ripper.sexp 'if y = x; z = y; end'
[:program,
 [[:if,
   [:assign,
    [:var_field, [:@ident, "y", [1, 3]]],
    [:vcall, [:@ident, "x", [1, 7]]]],
   [[:assign,
     [:var_field, [:@ident, "z", [1, 10]]],
     [:var_ref, [:@ident, "y", [1, 14]]]]],
   nil]]]

请注意,唯一真正的区别是引发NameError的示例使用:if_mod,而成功的版本使用:if。 看起来好像后条件是你看到的错误,怪癖或错误的原因。

该怎么做

这种解析行为可能有一个很好的技术原因,或者可能没有。 我没有资格评判。 但是,如果它看起来像是一个bug,并且您有动力去做某件事情,那么最好的办法是检查Ruby Issue Tracker以查看它是否已被报告。 如果没有,也许是有人正式提出来的。

链接地址: http://www.djcxy.com/p/81481.html

上一篇: Undefined local variable based on syntax in Ruby

下一篇: pylab/networkx; no node labels displayed after update