NameError:undefined - 在Ruby 2.1.2中更改了局部变量的解析规则吗?

saw*_*awa 6 ruby parsing local-variables variable-assignment guard-clause

我正在NameError: undefined local variable or method使用ruby 2.1.2

正如在这个问题中观察到的,表达式如:

bar if bar = true
Run Code Online (Sandbox Code Playgroud)

引发未定义的局部变量错误(前提bar是未定义),因为bar解析器在分配之前会读取它.而且我相信这个表达方式与以前没什么区别:

bar if bar = false
Run Code Online (Sandbox Code Playgroud)

两者之间的区别在于主体是否被评估,但是如果遇到未定义的局部变量,则在评估条件之前立即引发错误并不重要.

但是当我在Ruby 2.1.2上运行第二个代码时,它不会引发错误.它之前是这样吗?如果是这样,那么解析讨论的内容是什么?如果没有,Ruby规范是否已更改?有没有提到这个?它在1.8.7,1.9.3等中做了什么?

Jör*_*tag 5

是否bar定义没有区别。在这两种情况下,bar在体内都是未定义的。但是,在后一种情况下,永远不会评估主体,因此没关系。您永远不会解析名称bar,因此在名称解析期间永远不会出错。

解析分配时定义局部变量。它们在执行分配时被初始化。

变量被单数化是完全好的。nil在这种情况下,它将评估为:

if false
  bar = 42
end

bar
# => nil
Run Code Online (Sandbox Code Playgroud)

但是,如果变量未定义,则Ruby不知道裸字是局部变量还是无接收器的无参数消息发送:

foo
# NameError: undefined local variable or method `foo'
#                                     ^^^^^^^^^
# Ruby doesn't know whether it's a variable or a message send
Run Code Online (Sandbox Code Playgroud)

与之比较:

foo()
# NoMethodError: undefined method `foo'
# ^^^^^^^^^^^^^

self.foo
# NoMethodError: undefined method `foo'
# ^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

现在都在一起了:

foo()
# NoMethodError: undefined method `foo'

self.foo
# NoMethodError: undefined method `foo'

foo
# NameError: undefined local variable or method `foo'

if false
  foo = 42
end

foo
# => nil

foo = :fortytwo

foo
# => :fortytwo
Run Code Online (Sandbox Code Playgroud)

在这种特殊情况下的麻烦在于,解析表达式的顺序(以及因此定义变量的顺序)与执行表达式的顺序不匹配。

分配执行第一,你认为这会使得该bar会在体内进行定义。但这不是,因为首先对主体进行了解析,因此在看到分配之前,我不知道这是方法还是变量节点插入了语法树中。

但是,如果从未解释该节点,即条件为假,则不会发生任何不良情况。


Mic*_*ant 1

是的,它在 ruby​​ 2.1.2 中发生了变化

在1.8.7、 、中1.9.3,2.0.0甚至2.1.1我收到 2 个警告,但没有错误:

2.0.0-p247 :007 > bar if bar = false
(irb):7: warning: found = in conditional, should be ==
 => nil 
2.0.0-p247 :008 > bar if bar = true
(irb):8: warning: found = in conditional, should be ==
 => true 
Run Code Online (Sandbox Code Playgroud)

而在2.1.2你提到的版本中我收到 2 个警告和 1 个NameError错误。

2.1.2 :001 > bar if bar = true
(irb):1: warning: found = in conditional, should be ==
NameError: undefined local variable or method `bar' for main:Object
        from (irb):1
        from /home/durrantm/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :002 > bar if bar = false
(irb):2: warning: found = in conditional, should be ==
 => nil 
Run Code Online (Sandbox Code Playgroud)

这是在我的 Ubuntu 14 上

  • 大约 3 年来,rvm 一直是我实现这一目标的关键工具。我可以通过 `rvm use 2.1.2` 来切换。非常值得一试 - http://rvm.io/ 或只是 `\curl -sSL https://get.rvm.io | bash -s 稳定` (2认同)
  • 有些人已经切换到 rbenv 来管理他们的 ruby​​ 版本,但 rvm 可以满足我的需要,而且我没有看到切换的理由。 (2认同)