私はハッシュテーブルを持っています:
hash = Hash.new(0)
hash[:key] = hash[:key] + 1 # Line 1
hash[:key] += 1 # Line 2
ライン 1 とライン 2 は同じことを行います。行 1 ではキーによるハッシュを 2 回クエリする必要があるのに対し、行 2 では 1 回だけクエリを実行する必要があるようです。本当?それとも実際には同じですか?
私はハッシュテーブルを持っています:
hash = Hash.new(0)
hash[:key] = hash[:key] + 1 # Line 1
hash[:key] += 1 # Line 2
ライン 1 とライン 2 は同じことを行います。行 1 ではキーによるハッシュを 2 回クエリする必要があるのに対し、行 2 では 1 回だけクエリを実行する必要があるようです。本当?それとも実際には同じですか?
ベンチマーク用の Ruby スクリプトを作成しました
require 'benchmark'
def my_case1()
@hash[:key] = @hash[:key] + 1
end
def my_case2()
@hash[:key] += 1
end
n = 10000000
Benchmark.bm do |test|
test.report("case 1") {
@hash = Hash.new(1)
@hash[:key] = 0
n.times do; my_case1(); end
}
test.report("case 2") {
@hash = Hash.new(1)
@hash[:key] = 0
n.times do; my_case2(); end
}
end
これが結果です
user system total real
case 1 3.620000 0.080000 3.700000 ( 4.253319)
case 2 3.560000 0.080000 3.640000 ( 4.178699)
見た目hash[:key] += 1
は少し良くなりました。
Ruby 言語仕様では、省略されたインデックス割り当て式を評価するためのアルゴリズムが非常に明確に説明されています。それは次のようなものです:
primary_expression[indexing_argument_list] ω= expression
# ω can be any operator, in this example, it is +
(大まかに)次のように評価されます
o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
l << (v ω w)
o.[]=(*l)
特に、getter と setter の両方が 1 回だけ呼び出されていることがわかります。
また、非公式の脱糖を見てもわかります。
hash[:key] += 1
# is syntactic sugar for
hash[:key] = hash[:key] + 1
# which is syntactic sugar for
hash.[]=(:key, hash.[](:key).+(1))
ここでも、setter と getter の両方が 1 回だけ呼び出されていることがわかります。
2 つ目は、通常の方法です。より効率的です。