Ruby变量定义

jlh*_*ora 8 ruby interpreted-language ruby-on-rails abstract-syntax-tree

我偶然发现了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>'
Run Code Online (Sandbox Code Playgroud)

为什么不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
Run Code Online (Sandbox Code Playgroud)

在编译语言中,这甚至不会编译.

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

我真的不敢相信这是红宝石中的预期行为.并且它不是特定于irb的,在ruby/rails代码块中运行它会产生相同的结果.

Jör*_*tag 5

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

foo
Run Code Online (Sandbox Code Playgroud)

既可以表示“解引用一个局部变量”或“发送消息fooself不带参数的”,也就是说,它可以或者等同于

binding.local_variable_get(:foo)
Run Code Online (Sandbox Code Playgroud)

或者

self.foo()
# or
public_send(:foo)
Run Code Online (Sandbox Code Playgroud)

这种歧义是在解析时解决的。当解析器遇到对 的赋值时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的,磁悬浮和黄玉编译自己内部的字节码格式,然后编译为本地代码,JRuby的编译成JVM字节码( JVM 可能会或可能不会进一步编译),IronRuby 编译为 CIL 字节码(VES 可能会或可能不会进一步编译)。

Ruby 之所以有这种行为,是因为语言规范是这样说的。不是因为 Ruby 被“解释”了,因为实际上,它不是。Ruby 唯一的纯解释实现是 MRI 和 JRuby 的早期版本,两者都早已退役。