官方扩展 ||= 条件赋值运算符

6
我想强调我正在寻找Ruby 1.9.3解释器展开||=运算符的实际方式,而不是基于其行为看起来如何展开。我真正希望的是能够深入理解实际解释器源代码的人,这是一项我可能无法胜任的任务。我找到的唯一一个似乎涉及这个问题的资源已经过时了:“A short-circuit (||=) edge case”。
上面提到的资源似乎表明,在1.9之前的解释器版本中,“官方”的x ||= y展开成x = x || y要么是不准确的,要么存在缺陷。在任何情况下,所指出的边缘情况似乎已经得到解决。上面的资源声称x || x = yx or x = y更准确。但是,当x是以前未声明的全局变量时,这两种方法都不正确。
[11:04:18][****@asha:~]$ irb
1.9.3-p194 :001 > a || a = 3
    NameError: undefined local variable or method `a' for main:Object
1.9.3-p194 :002 > b or b = 3
    NameError: undefined local variable or method `b' for main:Object
1.9.3-p194 :003 > c = c || 3
    => 3 

在1.9.3中,至少就这些例子而言,x = x || y的展开似乎是正确的。然而,重申我的原始观点,我真的很希望看到一些真正权威的来源来权威地解决这个问题,而不是像我(和其他人)所做的那样凭经验判断。


可能是Is the ruby operator ||= intelligent?的重复问题。 - jamessan
2个回答

3
x ||= y

是的,这是一个缩写形式,表示

x || x = y

如果x不是nil并且x不是false,那么由于||运算符的短路评估,赋值将会发生。


2

编辑:本篇文章是关于规范的,阅读评论以了解不太理想的“实现故事”


Ruby草案规范(PDF)11.4.2.3.2节 对其进行了相当具体的定义(即使很难解释); 让我们用 c ||= 3; 的例子来说明一下(在理论上有点宽松)。

a)将变量作为变量引用(请参见11.5.4)进行评估。让V成为结果值。

V设置为c的值

b)评估操作符表达式或没有括号的方法调用。让W成为结果值。

W设置为3

c)让OP成为赋值运算符的赋值运算符名称。

OP被设置为||

d)让X成为形式为V OP W的操作符表达式。

X被设置为c || 3

e)让I成为缩略变量赋值表达式或缩略变量赋值语句的变量。

I被设置为引用c

f)评估单变量赋值表达式(请参见11.4.2.2.2),其中其变量为I,运算符表达式为X。

将评估c = c || 3

g)缩略变量赋值的值是评估的结果值。

赋值的结果是3

换句话说,扩展c = c || 3是正确的(除了像1.9之前的错误)。


很酷,这比某个家伙的博客文章让人更加安心。然而,还有一些后续问题:1)1.9.3解释器在多大程度上符合这个草案标准?(“草案”这个词让我稍微有点担心)2)如果我正确地阅读了全局变量引用部分,a)不会有任何问题。然而,11.5.4.5(类变量)似乎表明||=运算符不应该与尚未被赋值的类变量一起使用,但它确实可以,扩展也是如此。所以我对这一点有点困惑。 - cbmanica
1
哎呀,虽然我接受了正确链接到标准的答案,但显然在这种情况下标准是错误的。https://dev59.com/eXA75IYBdhLWcg3w_umD?lq=1有一个很好的反例来证明`x = x || y`扩展理论是错误的,因为如果x已经是“真实”的话,显然不会进行任何赋值操作。我也希望在提出这个问题之前能够看到http://www.ruby-forum.com/topic/151660/,因为那样我可能就不会增加到一个显然庞大的帖子列表中了。 - cbmanica
规范文件没有具体涉及||=。据我所知,这在MRI(以及其他实现中)是一个特例,为了保持兼容性。 - echristopherson
5
这是问题所在。||=&&=是特殊情况,但它们在规范中并没有特殊处理,这使得规范或者所有Ruby实现都有错。由于规范是专门编写的,旨在确保所有现有的Ruby实现都可以自动遵守而无需更改,因此错误显然出现在规范中。 - Jörg W Mittag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接