基于Ruby语法的未定义局部变量

前端之家收集整理的这篇文章主要介绍了基于Ruby语法的未定义局部变量前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在以下Ruby代码中,
#! /usr/bin/env ruby
x = true
y = x and z = y
puts "z: #{z}"

它将按预期输出z:true.

但是在下面的一个中,我希望它具有相同的行为:

#! /usr/bin/env ruby
x = true
z = y if y = x
puts "z: #{z}"

它导致了

undefined local variable or method ‘y’ for main:Object (NameError)

这是为什么?

我知道我正在做一个赋值,并隐式检查赋值以确定是否运行z = y.我也明白,如果我在x = 5行之后添加y,y = nil的声明,它将按预期传递并运行.

但是,期望语言首先评估if部分然后再评估其内容,并且第二块代码与第一块代码的行为相同,这是不正确的?

解决方法

TL; DR

这实际上是特定于解释器的.问题出现在MRI Ruby 2.1.2和JRuby 1.7.13中,但在Rubinius中按预期工作.例如,使用Rubinius 2.2.10:

x = true
z = y if y = x
#=> true

在MRI中,对Ripper的一点探索表明,即使AST的任务相似,Ruby也会对后置条件进行不同的处理.它实际上在构建AST时对后置条件使用不同的标记,这似乎对赋值表达式的求值顺序有影响.无论是否应该如此,或者是否可以修复,都是Ruby Core Team的问题.

为什么它适用于逻辑和

x = true
y = x and z = y

这是成功的,因为它实际上是顺序的两个赋值,因为true被赋值给x,因此评估为真实.由于第一个表达式是真实的,下一个表达式由逻辑连接并且也被评估,同样评估为真实.

y = x
#=> true

z = y
#=> true

换句话说,x被赋值为true,然后z也被赋值为true.在任何一项任务的右侧都没有定义.

为什么它在后置条件下失败

x = true
z = y if y = x

在这种情况下,实际上首先评估后置条件.你可以通过查看AST看到这个:

require 'pp'
require 'ripper'

x = true

pp Ripper.sexp 'z = y if y = x'
[:program,[[:if_mod,[:assign,[:var_field,[:@ident,"y",[1,9]]],[:vcall,"x",13]]]],"z",0]]],4]]]]]]]

与第一个示例不同,其中y在第一个表达式中被赋值为true,因此在分配给z之前在第二个表达式中解析为true,在这种情况下,y仍在未定义的情况下进行求值.这提高了NameError.

当然,人们可以合理地争辩说两个表达式都包含赋值,并且如果Ruby的解析器首先评估y = x,并且正常if语句(参见下面的AST),那么y就不会真正被定义.这可能只是后置条件if语句和Ruby处理:if_mod标记的方式的怪癖.

成功:if if而不是:if_mod Tokens

如果您反转逻辑并使用普通的if语句,它可以正常工作:

x = true
if y = x
  z = y
end
#=> true

看着开膛手产生以下AST:

require 'pp'
require 'ripper'

x = true

pp Ripper.sexp 'if y = x; z = y; end'
[:program,[[:if,3]]],7]]]],[[:assign,10]]],[:var_ref,14]]]]],nil]]]

请注意,唯一真正的区别是引发NameError的示例使用:if_mod,而成功使用的版本使用:if.当然,后期条件是您所看到的错误,怪癖或错误的原因.

怎么办呢

这种解析行为可能有很好的技术原因,或者可能没有.我没资格判断.但是,如果它看起来像你的错误,并且你有动力去做一些事情,那么最好的办法就是检查Ruby Issue Tracker以查看它是否已被报告.如果没有,也许是时候有人正式提出来了.

原文链接:https://www.f2er.com/ruby/274294.html

猜你在找的Ruby相关文章