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

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

 'never shown' if (false) 

并且可以在if语句中进行赋值。

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

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

如果所有这些假设都是真的,为什么会失败呢?

 error_array < undefined local variable or method `error' for 

它在数组推右前分配给错误? 我想了解什么时候进行评估。

这个确实有效:

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

现在我真的很困惑。

它只会在您尝试分配文字值时发生,如果您调用它起作用的函数。

 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)) 

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

Ruby操作顺序

在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() )而不是局部变量。

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

 array << error if (error = some_function) 

然后逻辑上,执行应该是这样的

  1. some_function
  2. 如果未定义error ,则定义error
  3. some_function返回值赋给error
  4. 评估if
  5. 如果if计算结果为trueerror值附加到array

然而,解析明智(假设典型的LR解析器),它去

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