我偶然发现了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
在编译语言中,这甚至无法编译。
我真的不敢相信这是红宝石中的预期行为。并且它不是特定于irb的,在ruby / rails代码块中运行它会产生相同的结果。
答案 0 :(得分:3)
在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
。)
在编译语言中,这甚至无法编译。
没有编译语言这样的东西。编译和解释是编译器或解释器(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编译成自己的内部字节码格式,然后编译< em> 到本机代码,JRuby编译为JVM字节码(JVM可能会或可能不会进一步编译),IronRuby编译为CIL字节码(VES可能会或可能不会进一步编译)。
Ruby以这种方式运行的事实是因为语言规范这样说。不是因为Ruby是“解释”的,因为实际上并非如此。唯一纯粹解释的Ruby实现是MRI和JRuby的早期版本,两者都早已退役。
答案 1 :(得分: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 >
区别在于Global和Instance变量。即使它们尚未定义,它们也会自动取值为零。