我正在if
块内玩分配操作,并发现了以下结果,这让我感到惊讶:
C:\>irb --simple-prompt
if false
x = 10
end
#=> nil
p x
nil
x.object_id
#=> 4
#=> nil
p y
NameError: undefined local variable or method `y' for main:Object
from (irb):5
from C:/Ruby193/bin/irb:12:in `<main>'
在上面的代码中,您可以看到x
局部变量已创建,即使它仅在伪造的if
块中分配。我试图查看x
与p x
的内容,这迫使我相信分配没有完成,但x
变量存在。 x.object_id
也证明了这种情况。
现在我的问题是,即使x
块入口点是否被永久关闭,如何创建if
局部变量?
我希望p x
的输出与p y
的输出相似。但我得到了p x
的惊人答案。
有人可以向我解释这个概念是如何运作的吗?
修改
不,这是另一项测试。仅local
个变量不是这种情况。 instance
和class
变量也是如此。见下文:
class Foo
def show
@X = 10 if false
p @X,"hi",@X.object_id
end
end
#=> nil
Foo.new.show
nil
"hi"
4
#=> [nil, "hi", 4]
class Foo
def self.show
@@X = 10 if false
p @@X,"hi",@@X.object_id
end
end
#=> nil
Foo.show
nil
"hi"
4
#=> [nil, "hi", 4]
成功案例:
class Foo
def self.show
@@X = 10 if true
p @@X,"hi",@@X.object_id
end
end
#=> nil
Foo.show
10
"hi"
4
#=> [10, "hi", 4]
答案 0 :(得分:8)
在Ruby中,局部变量在首次遇到赋值时由解析器定义,然后从该点开始在范围内。
这是一个小小的演示:
foo # NameError: undefined local variable or method `foo' for main:Object
if false
foo = 42
end
foo # => nil
如您所见,即使第4行的赋值从未执行过,第7行也存在局部变量 。然而,它是解析,这就是本地变量foo
存在的原因。但由于赋值从未执行过,因此该变量未初始化,因此评估为nil
而不是42
。
在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
是不明确的:它可能是< em>要么无参数消息发送到默认接收器(即等效于self.fnord()
)或对本地变量fnord
的访问。
为了消除歧义,你需要添加一个接收器或一个参数列表(即使是空的)来告诉Ruby它是一个消息发送:
self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object
或确保解析器(不是评估者)在使用之前解析( not 执行)一个赋值,告诉Ruby它是一个局部变量:
if false
fnord = 42
end
fnord #=> nil
当然,nil
是一个对象(它是类NilClass
的唯一实例),因此具有object_id
方法。
答案 1 :(得分:3)
Ruby总是会解析你的所有代码。它不会将false视为不解析内部内容的标志,它会对其进行评估并发现内部代码不应被执行
答案 2 :(得分:1)
Ruby有局部变量“hoisting”。如果在方法中的任何位置都有对局部变量的赋值,那么该变量在方法中的任何位置都存在,甚至在赋值之前,即使从未实际执行赋值。在分配变量之前,它的值为nil
。
编辑:
以上不太正确。 Ruby确实有一种变量提升形式,它会在存在局部变量赋值时定义局部变量,但不会执行。但是,不会发现在上面发生分配的方法中的点定义变量。