Ruby 变量定义

Ruby variable definition

我在 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 中的预期行为。而且它不是特定于 irb 的,运行 它在 ruby/rails 代码块中给出相同的结果。

我在这一点上可能是错的,但是 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 的值。

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

foo

可以表示 "dereference a local variable" 或 "send message foo to self without arguments",即它可以等同于

binding.local_variable_get(:foo)

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

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

In a compiled language this wouldn't even compile.

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

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

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

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

Ruby 这样的行为是因为语言规范是这样说的。不是因为 Ruby 是 "interpreted",因为实际上它不是。 Ruby 的唯一纯解释实现是 MRI 和 JRuby 的早期版本,两者都早已退休。