为了账号安全,请及时绑定邮箱和手机立即绑定

虚假的“ if”块中的赋值操作令人困惑

/ 猿问

虚假的“ if”块中的赋值操作令人困惑

123456qqq 2019-10-22 21:22:22

我在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即使仅在falsy 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]


查看完整描述

3 回答

?
慕娘9325324

在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不明确:它可能是任何一个参数少消息发送到默认的接收器(即相当于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

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


查看完整回答
反对 回复 2019-10-22
?
若吾皇

Ruby总是解析所有代码。它不会将false视为不解析内部内容的标志,它会对其进行评估并发现不应执行内部代码


查看完整回答
反对 回复 2019-10-22
?
12345678_0001

Ruby具有局部变量“提升”。如果在方法中的任何地方都对局部变量进行了赋值,则该变量在方法中的任何地方都存在,甚至在赋值之前,甚至从未真正执行过赋值。在分配变量之前,其值为nil。


编辑:


以上不是很正确。Ruby确实有一种变量提升的形式,即当存在局部变量赋值但不执行时,它将定义一个局部变量。但是,在上述方法中发生赋值的点上找不到该变量。


查看完整回答
反对 回复 2019-10-22

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信