shorthand 像 ruby 中的 (+=) 这样的运算符到底有什么作用?

What does shorthand operator like (+=) in ruby really does?

虽然我可以理解 x += 1 等同于 x = x + 1,但我对它在后台的作用很感兴趣。

我在 irb 中尝试过 x += 1,但没有先给 x 赋值,我肯定得到一个错误,它说

NoMethodError (undefined method `+' for nil:NilClass)

当我在出错后尝试检查 x 值时,它现在是一个 nil 值,它在分配时不太清楚,除非它是由 x += 1 语句完成的。

所以,我试图了解 += 是如何做的,因为我找不到它的文档,或者它被埋在 ruby 类 的后面。任何指针将不胜感激。

Ruby 如果看到赋值 (=) 则将标识符视为变量,即使赋值失败也是如此。

irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
    from (irb):1
    from /usr/bin/irb:12:in `<main>'
irb(main):002:0> a = 1 if false
=> nil
irb(main):003:0> a
=> nil
irb(main):004:0>

即使 a=1 从不执行,a 也会默认为 nil

分析

定义我们的条款

首先,++=是方法,不是keywords or operators. More accurately, + is defined as Integer#+,而+=是解释器提供的语法糖,作为shorthand递增赋值等常见操作。

其次,+=及相关操作在Ruby中调用abbreviated assignments。当前支持的作业 shorthand 项的完整列表包括:

["+=",
 "-=",
 "*=",
 "/=",
 "%=",
 "**=",
 "&=",
 "|=",
 "^=",
 "<<=",
 ">>=",
 "||=",
 "&&="]

Ruby中唯一的non-method operators是:

["=", "..", "...", "!", "not", "&&", "and", "||", "or", "!=", "!~"]

和 shorthand 赋值运算符,如 += 不是用户可定义的。

定义问题

您看到的行为与作用域以及解析器如何处理块局部变量有关。具体来说,Ruby assignment documentation 表示:

[A] local variable is created when the parser encounters the assignment, not when the assignment occurs[.]

这意味着顶级代码如:

x
#=> NameError (undefined local variable or method `x' for main:Object)

引发 NameError 异常,因为 defined? x #=> nil。但是,解析器会在分配时自动实例化局部变量,以便以下工作(某种程度上),至少在它不会引发 NameError:

的意义上
# x is autovivified as nil
x = x + 1
#=> NoMethodError (undefined method `+' for nil:NilClass)

但是,nil 没有 NilClass#+ 方法,因此您得到的是 NoMethodError 而不是 NameError。这是因为:

nil.respond_to? :+
#=> false

解决方案

基本解决方案是明确确保您的变量存在于当前范围内,并且它不是 nil。您可以通过多种方式执行此操作,但最简单的方法就是直接分配它。例如:

# assign zero to x unless it's truthy
x ||= 0

# perform incrementing assignment
x += 1

或者,您可以删除 shorthand 并将自动生成的 x 转换为整数, 有一个 :+方法。例如,假设 x 当前未定义(例如,您已重新启动 irb,或者已将 x 设置为 nil):

x = x.to_i + 1
#=> 1