はじめに
Rubyを触り始めて結構長いのですが、今更ながら||=
についての理解を間違えていることに気がついたため、この記事で理解を深めようと思います。
||=
とは
自己代入の一種です。
例えば、a ||= 10
という式は a || (a = 10)
と解釈され、aが偽もしくは未定義であれば10を代入する
という意味になります。
変数を初期化する際のイディオムの一種です。
何を間違えていたのか
a = a || 10
と解釈されると勘違いしていました。
a += 10
やa -= 10
などはa = a + 10
、a = a - 10
と解釈されるため、それと同じと思い込んでいました。
どう違うのか
再代入されるかどうかが違ってきます。
a || (a = 10)
の場合、a
が真であれば再代入されませんが、a = a || 10
の場合、a
が真であっても再代入が行われます。
そのため、例えば以下のような、ある属性に代入した数を数えるようなコードがあったとすると、結果が違ってきます。
コード
class Sample
attr_reader :assign_num, :value
def value=(v)
@value = v
@assign_num = @assign_num.to_i + 1 # 代入された回数を保持する
end
end
a = Sample.new
5.times do
a.value = a.value || 10 # この場合は再代入される。
end
puts "a.assign_num: #{a.assign_num} a.value: #{a.value}"
b = Sample.new
5.times do
b.value ||= 10 # この場合は b.value || (b.value = 10) と解釈されるため再代入されない
end
puts "b.assign_num: #{b.assign_num} b.value: #{b.value}"
出力結果
a.assign_num: 5 a.value: 10
b.assign_num: 1 b.value: 10
代入がトリガーになるようなプログラムの場合、挙動を理解しておかないと想定した動きにならないかもしれません。
まとめ
-
a ||= 10
はa = a || 10
ではなく、a || (a = 10)
と解釈される。 - 上記の違いは再代入が行われるかどうか。
- 代入がトリガーになるようなプログラムの場合は挙動に注意が必要。
もう5年くらい触っている気がしますが、ここにきて知った事実でした。
参考