Ruby变量定义



我在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代码块中运行它会得到相同的结果。

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

foo

可以表示"取消引用本地变量"或"将消息foo发送到不带参数的self",即它可以等效于

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的早期版本,这两个版本早就退役了。

我可能错了,但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的值。

相关内容

  • 没有找到相关文章

最新更新