我有一个哈希表:
hash = Hash.new(0)
hash[:key] = hash[:key] + 1 # Line 1
hash[:key] += 1 # Line 2
第 1 行和第 2 行做同样的事情。看起来第 1 行需要按键查询哈希两次,而第 2 行只需查询一次。真的吗?或者他们实际上是一样的?
我有一个哈希表:
hash = Hash.new(0)
hash[:key] = hash[:key] + 1 # Line 1
hash[:key] += 1 # Line 2
第 1 行和第 2 行做同样的事情。看起来第 1 行需要按键查询哈希两次,而第 2 行只需查询一次。真的吗?或者他们实际上是一样的?
我创建了一个 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
稍微好一点。
@sza 打败了我 :)
这是我的示例irb
会话:
> require 'benchmark'
=> true
> n = 10000000
=> 10000000
> Benchmark.bm do |x|
> hash = Hash.new(0)
> x.report("Case 1:") { n.times do; hash[:key] = hash[:key] + 1; end }
> hash = Hash.new(0)
> x.report("Case 2:") { n.times do; hash[:key] += 1; end }
> end
user system total real
Case 1: 1.070000 0.000000 1.070000 ( 1.071366)
Case 2: 1.040000 0.000000 1.040000 ( 1.043644)
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 都只调用了一次。
您还可以通过查看非正式的脱糖来看到这一点:
hash[:key] += 1
# is syntactic sugar for
hash[:key] = hash[:key] + 1
# which is syntactic sugar for
hash.[]=(:key, hash.[](:key).+(1))
同样,您会看到 setter 和 getter 都只调用了一次。
第二个是习惯的做法。它更有效。