2

在 Ruby 中工作时,我经常发现自己在使用带有 a的方法!或使用普通方法来分配值之间存在冲突。我不确定何时使用什么。例如,我有 2 个哈希(h1 和 h2),我想合并它们并将值存储回哈希 h1,现在我应该使用 h1.merge!(h2) or h1 = h1.merge(h2)吗?

两者有什么区别吗?

4

3 回答 3

7

h1.merge!(h2)大多数时候和之间几乎没有区别h1 = h1.merge(h2)

但是,请注意:

由于merge!修改了旧的哈希,您可能会无意中影响程序中持有对同一哈希的引用的其他一些对象。修改作为方法参数收到的散列是不好的做法,因为调用者通常不期望它。

使用merge!不是函数式编程,如果你是它的粉丝的话。

使用merge!可能更有效,因为它不会创建新的散列,尤其是对于大散列。

merge大部分时间都会使用,并且只有merge!在我确定它安全且更好时才使用。

于 2013-09-15T18:01:07.077 回答
2

两者有什么区别吗?

是的,当然有。

当您想更改接收器本身时,您应该使用!(bang) 版本。Hash#merge

例子

h1 = {a: 1}
h2 = {b: 2}
h3 = h1.merge(h2) # => {:a=>1, :b=>2}
h1 # => {:a=>1}

现在看:

h1 = {a: 1}
h2 = {b: 2}
h1.merge!(h2) # => {:a=>1, :b=>2}
h1 # => {:a=>1, :b=>2}

h1 = h1.merge(h2) 给出相同的答案

嗯,那是因为,您h1在应用该Hash#merge方法后将新哈希分配给:

h1 = {a: 1}
h2 = {b: 2}
h1.object_id # => 69435570
h1 = h1.merge(h2) # => {:a=>1, :b=>2}
h1 # => {:a=>1, :b=>2}
h1.object_id # => 69434820

正如您所说,我想合并它们并将值存储回哈希 h1中,然后我建议使用Hash#merge!。因为h1 = h1.merge(h2)h1.merge!(h2)(将保存新的哈希创建,并将其分配回h1)相同。

于 2013-09-15T17:47:37.107 回答
2

在 Ruby 中工作时,我经常发现自己在使用带有 ! 或使用常规方法。

你应该考虑更重要的事情,所以只要遵守规则:我永远不会使用 bang 方法。现在自由和翱翔......

require 'benchmark'

n = 1_000_000

h1 = {a: 1, b: 2}
h2 = {b: 3, c: 4}

Benchmark.bm(20) do |b|
  b.report("no-bang-hash-merge") do
    n.times { h1 = h1.merge h2 }
  end

  b.report("bang-hash-merge") do
    n.times { h1.merge! h2 }
  end
end

--output:--
                          user     system      total        real
no-bang-hash-merge     2.750000   0.050000   2.800000 (  2.817345)
bang-hash-merge        0.400000   0.000000   0.400000 (  0.406870)

.

require 'benchmark'

hash_size = 10_000

#Keys overlap:
key1 = 'a'
key2 = nil

h1 = {}
hash_size.times do |i|
  h1[key1] = i
  key2 = key1.dup if i == hash_size/2
  key1.succ!
end


h2 = {}
hash_size.times do |i|
  h2[key2] = i
  key2.succ!
end

=begin
#No overlap:
key = 'a'

h1 = {}
hash_size.times do |i|
  h1[key] = i
  key.succ!
end


h2 = {}
hash_size.times do |i|
  h2[key] = i
  key.succ!
end
=end

n = 100_000

puts "50% of keys overlap, hash size #{hash_size}:"
Benchmark.bm(20) do |b|
  b.report("no-bang-hash-merge") do
    n.times { h1 = h1.merge h2 }
  end

  b.report("bang-hash-merge") do
    n.times { h1.merge! h2 }
  end
end

--some test runs:---

50% of keys overlap, hash size 10000:
                           user     system      total        real
no-bang-hash-merge   1500.570000  74.520000 1575.090000 (1695.523240)
bang-hash-merge      255.910000   0.940000 256.850000 (269.957178)



No keys overlap, hash size 10000:
                           user     system      total        real
no-bang-hash-merge   1906.070000 109.340000 2015.410000 (2151.865636)
bang-hash-merge      162.680000   0.190000 162.870000 (163.369607)

因此,如果您需要速度,请远离。否则,不要冒险。

于 2013-09-15T18:22:40.840 回答