解析与操作的可变范围和顺序:“if”中的赋值

时间:2013-02-27 01:49:52

标签: ruby variable-assignment conditional-statements

我的理解是该行末尾的if语句在该行前面的代码之前进行了评估:

'never shown' if (false)

可以在if声明中进行分配。

'shown' if (value = 'dave is king')
value #=> "dave is king"

并且,当分配了不存在的变量时,会创建它。事先不需要它存在。这是真的吗?

如果所有这些假设都成立,为什么会失败?

error_array << error if (error = import_value(value))
#=> undefined local variable or method `error' for

在阵列向右推之前分配给错误?我想了解什么时候进行评估。

这个确实有效:

if (error = import_value(value))
  error_array << error
end

现在我真的很困惑。

3 个答案:

答案 0 :(得分:5)

只有在尝试分配文字值时才会发生这种情况,如果你调用它有效的函数。

def foo(a)
  a
end

p 'not shown' if(value = foo(false))
p 'shown' if(value = foo(true))

# This outputs a Warning in IRB
p 'shown' if(value = false)
(irb):2: warning: found = in conditional, should be ==

如果打开调试(-d),您会看到有关已使用变量value的警告

warning: assigned but unused variable - value

这个“有效”,因为该语句确实评估为true,并允许其前面的代码运行。

这里发生的是if()当用作修饰符时它有自己的绑定范围或上下文。因此,在if之外永远不会看到赋值,因此执行起来没有任何意义。这与控制结构不同,因为if语句所采用的块也与赋值在同一范围内,而if修饰符之前的行不在if的范围内。

换句话说,这些并不等同。

if a = some(value)
  puts a
end

puts a if(a = some(value))

前者在if的范围内有puts a,后者在范围之外有puts a,因此具有不同的绑定(ruby称之为上下文)。

Ruby Order of Operations

答案 1 :(得分:4)

在Ruby中,局部变量在首次遇到赋值时由解析器定义,然后从该点开始在范围内。由于Ruby被解析为英语,从左到右,从上到下,当你使用它时,本地变量还不存在,因为使用还远离了赋值。

这是一个小小的演示:

foo # NameError: undefined local variable or method `foo' for main:Object

if false
  foo = 42
end

foo # => nil

如您所见,即使第4行的赋值从未执行过,第7行也存在局部变量 。然而,它是解析,这就是本地变量foo存在的原因。但由于赋值从未执行过,因此该变量未初始化,因此评估为nil而不是42

现在让我们来看看你案例的最简单版本:

bar if bar = true
# warning: found = in conditional, should be ==
# NameError: undefined local variable or method `bar' for main:Object

bar # => true

在解析赋值时创建变量,该位于:

bar if bar = true
       ^^^^^^^^^^

但它在这里使用:

bar if bar = true
^^^

在分配之前。在使用之前执行分配的事实是无关紧要的,因为解析在这里是相关的,并且在使用之后分配在使用时,解析器仍然认为它是一个没有参数列表和隐式接收器的方法调用(即相当于self.bar()),而不是局部变量。

答案 2 :(得分:2)

我的猜测是解析的顺序与(逻辑)执行顺序不同。特别是,给定

array << error if (error = some_function)

然后逻辑上,执行应该像

  1. 致电some_function
  2. 如果error未定义,请定义error
  3. some_function的返回值指定为error
  4. 评估if
  5. 如果if评估为true,则将error的值附加到array
  6. 然而,解析明智(假设是典型的LR解析器),它就是

    1. 获得了令牌array(标识符)。这个定义了吗?是。它是变量吗?是。
    2. 获得了令牌<<(运营商)。 array会回复<<吗?是(否则,输出“未定义方法”错误)。
    3. 获得了令牌error(标识符)。这个定义了吗?编号输出“未定义的局部变量或方法”。