1

我有一个哈希表:

hash = Hash.new(0)

hash[:key] = hash[:key] + 1   # Line 1
hash[:key] += 1               # Line 2

第 1 行和第 2 行做同样的事情。看起来第 1 行需要按键查询哈希两次,而第 2 行只需查询一次。真的吗?或者他们实际上是一样的?

4

4 回答 4

4

我创建了一个 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稍微好一点。

于 2013-08-22T03:24:49.843 回答
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)
于 2013-08-22T03:28:22.917 回答
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 都只调用了一次。

您还可以通过查看非正式的脱糖来看到这一点:

hash[:key] += 1

# is syntactic sugar for 

hash[:key] = hash[:key] + 1

# which is syntactic sugar for 

hash.[]=(:key, hash.[](:key).+(1))

同样,您会看到 setter 和 getter 都只调用了一次。

于 2013-08-22T09:16:28.830 回答
0

第二个是习惯的做法。它更有效。

于 2013-08-22T03:13:44.693 回答