在Ruby中,为什么在启动irb后,foo.nil?会显示未定义错误,@foo.nil?会返回"true",而@@wah.nil?又会再次出现错误?

13

在Ruby 1.8.7和1.9.2中相同:

$ irb

ruby-1.8.7-p302 > foo.nil?
NameError: undefined local variable or method `foo' for #<Object:0x3794c>
    from (irb):1

ruby-1.8.7-p302 > @bar.nil?
 => true 

ruby-1.8.7-p302 > @@wah.nil?
NameError: uninitialized class variable @@wah in Object
    from (irb):3

为什么实例变量与局部变量和类变量有不同的对待?

1个回答

17
在 Ruby 中,大部分未初始化或不存在的变量都会被评估为 nil。这适用于局部变量、实例变量和全局变量。
defined? foo       #=> nil
local_variables    #=> []
if false
  foo = 42
end
defined? foo       #=> 'local-variable'
local_variables    #=> [:foo]
foo                #=> nil
foo.nil?           #=> true

defined? @bar      #=> nil
instance_variables #=> []
@bar               #=> nil
@bar.nil?          #=> true
# warning: instance variable @bar not initialized

defined? $baz      #=> nil
$baz               #=> nil
# warning: global variable `$baz' not initialized
$baz.nil?          #=> true
# warning: global variable `$baz' not initialized

然而,对于类层次结构中的变量和常量,情况并非如此:

defined? @@wah     #=> nil
@@wah
# NameError: uninitialized class variable @@wah in Object

defined? QUUX      #=> nil
QUUX
# NameError: uninitialized constant Object::QUUX

这是一个误导:

defined? fnord     #=> nil
local_variables    #=> []
fnord
# NameError: undefined local variable or method `fnord' for main:Object
你之所以会在这里得到错误,并不是因为未初始化的本地变量不会评估为nil,而是因为fnord是模糊不清的:它可能是发送给默认接收器的无参数消息(即等同于self.fnord()),也可能是对本地变量fnord的访问。
为了消除歧义,你需要添加一个接收器或一个参数列表(即使为空)来告诉Ruby它是一条消息发送。
self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object

或者确保解析器(而不是求值器)在使用前对赋值进行解析(而不是执行),以告诉Ruby它是一个局部变量:

if false
  fnord = 42
end
fnord              #=> nil

为什么实例变量与本地变量和类变量不同?

实际上它并没有不同,它的处理方式与本地变量相同。类层次结构变量表现得不同,但本地变量、实例变量和全局变量都是相同的。

是否有其他原因…类变量不能像这样表现吗?

我不知道。对于实例变量来说,这非常方便,因为与 Java 不同,例如,在 Java 中,实例变量在类定义中声明,因此在类的每个实例中始终存在。而在 Ruby 中,实例变量没有在任何地方声明。只要被分配了,它们就会神奇地出现。由于实例变量可能不存在,如果它们抛出异常,编写使用实例变量的方法将很麻烦。

为什么类层次结构变量不同,我不知道。也许是因为没人使用它们,或者因为它们通常在类体中初始化并且未初始化时不被访问。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接