混淆了在一个falsy的`if`块中的赋值操作

我正在玩块内的分配操作,发现了下面的结果,这让我感到吃惊:

 C:\>irb --simple-prompt if false x = 10 end #=> nil px nil x.object_id #=> 4 #=> nil py NameError: undefined local variable or method `y' for main:Object from (irb):5 from C:/Ruby193/bin/irb:12:in `<main>' 

在上面的代码中,你可以看到x局部variables已经被创build,即使它只被分配给falsy if块。 我试图用px来查看x的内容,这迫使我相信赋值没有完成,但存在xvariables。 x.object_id也certificate了是这样的。

现在我的问题是如何创buildx局部variables即使if区块入口点被永远closures的故意?

我期望px的输出与py的输出相似。 但相反,我从px得到了一个令人惊讶的答案。

有人能向我解释这个概念是如何工作的吗?

编辑

不,这是另一个testing。 只有localvariables不是这种情况。 instanceclassvariables也是如此。 请看下面:

 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] 

在Ruby中,局部variables是在parsing器首次遇到赋值时定义的,然后在这个范围内。

这里有一个示例:

 foo # NameError: undefined local variable or method `foo' for main:Object if false foo = 42 end foo # => nil 

正如你所看到的,即使第4行的赋值从未被执行,局部variables存在于第7行。 然而,它被parsing ,这就是为什么局部variablesfoo存在。 但是因为这个赋值从来没有被执行过,所以这个variables是未初始化的,因此它的值nil而不是42

在Ruby中,大多数未初始化或甚至不存在的variables的计算结果nil 。 对于局部variables,实例variables和全局variables,这是正确的:

 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 

但是,类层次结构variables和常量不是这样的:

 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 

你在这里得到一个错误的原因并不是单元化的局部variables不计算nil ,而是fnord是模棱两可的:它可能是一个无参数的消息发送到默认的接收器(即相当于self.fnord()或者访问局部variablesfnord

为了消除歧义,你需要添加一个接收者或者一个参数列表(即使是空的)来告诉Ruby它是一个消息发送:

 self.fnord # NoMethodError: undefined method `fnord' for main:Object fnord() # NoMethodError: undefined method `fnord' for main:Object 

或者确保parsing器不是评估者)在使用之前parsing( 执行)赋值,以告知Ruby它是一个局部variables:

 if false fnord = 42 end fnord #=> nil 

而且,当然, nil是一个对象(它是类NilClass的唯一实例),因此有一个object_id方法。

Ruby总是分析你的所有代码。 它不会将虚假看作是不parsing内部的标志,而是评估它,并且看到里面的代码不应该被执行

Ruby有局部variables“提升”。 如果你在一个方法的任何地方有一个局部variables的赋值,那么这个variables就存在于方法中的任何地方,甚至在赋值之前,即使赋值永远不会被实际执行。 在分配variables之前,它的值nil

编辑:

以上是不正确的。 Ruby确实有一种forms的variables提升,因为当局部variables赋值出现时,它会定义一个局部variables,但是不会被执行。 然而,这个variables不会被定义在上面的分配方法中的点上。