9

我偶然发现了 ruby​​ 中关于变量定义的一个奇怪行为(并且在途中丢失了一盒甜甜圈):

irb(main):001:0> if false
irb(main):002:1>   a = 1
irb(main):003:1> end
=> nil
irb(main):005:0> a.nil?
=> true
irb(main):006:0> b.nil?
NameError: undefined local variable or method `b' for main:Object
    from (irb):6
    from /Users/jlh/.rbenv/versions/2.1.5/bin/irb:11:in `<main>'

为什么不a.nil?undefined local variable?例如,看一下 python(只是想将其与解释语言进行比较):

>>> if False:
...     a = 1
... 
>>> print a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

在编译语言中,这甚至无法编译。

  • 这是否意味着 ruby​​ 会保留对该变量的引用,即使它没有通过那段代码?
  • 如果是这样,变量定义考虑的 ifs/else 有多深?

我真的不敢相信这是 ruby​​ 的预期行为。而且它不是特定于 irb 的,在 ruby​​/rails 代码块中运行它会得到相同的结果。

4

2 回答 2

5

在 Ruby 中,引用局部变量和发送到没有参数列表的隐式接收器的消息之间存在歧义。这意味着

foo

可以表示“取消引用局部变量”或“不带参数发送消息fooself,即它可以等同于

binding.local_variable_get(:foo)

或者

self.foo()
# or
public_send(:foo)

这种歧义在解析时得到解决。当解析器遇到对 的赋值时,从那时起foo,它将被foo视为局部变量,而不管赋值是否实际执行。(毕竟,这是解析器无法静态确定的。想想吧if rand > 0.5 then foo = 42 end。)

在编译语言中,这甚至无法编译。

没有编译语言这样的东西。编译和解释是编译器或解释器的特征(呃!)而不是语言。语言既不编译也不解释。他们只是

每种语言都可以用编译器实现,每种语言都可以用解释器实现。大多数语言都有编译和解释两种实现(例如,C 有 GCC 和 Clang,它们是编译器,Cint 和 Cling 是解释器,Haskell 有 GHC,它是编译器,Hugs 是解释器)。

许多现代语言实现都在相同的实现中,或者在不同的阶段(例如,YARV 和 MRuby 将 Ruby 源代码编译为内部字节码,然后解释该字节码),或者在混合模式引擎中(例如,HotSpot JVM 既解释又编译JVM 字节码,取决于哪个更有意义),或两者兼有(例如,Rubinius 在第一阶段将 Ruby 源代码编译为 Rubinius 字节码,然后都将该字节码编译为本机代码并解释它,这取决于更有意义的方式)。

事实上,所有现有的 Ruby 实现都被编译:YARV 和 MRuby 编译成它们自己的内部字节码格式,Rubinius、MacRuby、MagLev 和 Topaz 编译成它们自己的内部字节码格式,然后编译成本代码,JRuby 编译成 JVM 字节码( JVM 可能会或可能不会进一步编译),IronRuby 编译为 CIL 字节码(VES 可能会或可能不会进一步编译)。

Ruby 以这种方式运行的事实是因为语言规范是这样说的。不是因为 Ruby 是“解释”的,因为实际上它不是。Ruby 的唯一纯解释型实现是 MRI 和非常早期的 JRuby 版本,两者都早已退役。

于 2015-02-27T15:11:14.383 回答
1

我可能错了,但是 Ruby 为你的变量定义了范围。您具有全局范围,即 $

然后你就有了运行脚本的本地范围,这是你在问题中演示的。您可以在方法中定义变量,但它仍然可以在运行脚本的本地范围内使用。

来源:http ://ruby-doc.org//docs/ruby-doc-bundle/UsersGuide/rg/localvars.html

这里说明了局部变量不具有 nil 的初始值,但是一旦被定义,就取它们所具有的任何值,而不管被定义。

2.1.5 :001 > p 1
1
 => 1 
2.1.5 :002 > p a
NameError: undefined local variable or method `a' for main:Object
    from (irb):2
    from /Users/deh0002a/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
2.1.5 :003 > if false
2.1.5 :004?>   a = 2
2.1.5 :005?>   else
2.1.5 :006 >     a = 3
2.1.5 :007?>   end
 => 3 
2.1.5 :008 > p a
3
 => 3 
2.1.5 :009 > p$a
nil
 => nil 
2.1.5 :010 > p @a
nil
 => nil 
2.1.5 :011 > 

区别再次在于全局变量和实例变量。即使它们没有被定义,它们也会自动取 nil 的值。

于 2015-02-27T13:19:41.267 回答