4

以下の例を参照してください

require "set"
s = [[1, 2], [3, 4]].to_set # s = {[1, 2], [3, 4]}
m = s.max_by {|a| a[0]} # m = [3, 4]
m[0] = 9 # m = [9, 4], s = {[1, 2], [9, 4]}
s.delete(m) # s = {[1, 2], [9, 4]} ?????

これは、配列とは異なる動作をします。( を削除する.to_setと、期待どおりの結果が得られs = [[1, 2]]ます。) これはバグですか?

4

1 に答える 1

7

はい、これはバグです。少なくとも私はバグと呼んでいます。これを「実装の詳細が誤って外の世界に漏れた」と呼ぶ人もいますが、それはバグのための空想的なズボンの都市少年の話です。

この問題には、次の 2 つの主な原因があります。

  1. Set が知らないうちに Set の要素を変更しています。
  2. 標準の Ruby Setはハッシュとして実装されています。

その結果、ハッシュがそれを認識せずに内部ハッシュのキーを変更しているため、貧弱なハッシュが混乱して、それがもはやどのキーを持っているのか本当にわからなくなります。Hash クラスには次のrehashメソッドがあります。

再ハッシュ → hsh

各キーの現在のハッシュ値に基づいてハッシュを再構築します。挿入後にキー オブジェクトの値が変更された場合、このメソッドはhsh のインデックスを再作成します。

a = [ "a", "b" ]
c = [ "c", "d" ]
h = { a => 100, c => 300 }
h[a]       #=> 100
a[0] = "z"
h[a]       #=> nil
h.rehash   #=> {["z", "b"]=>100, ["c", "d"]=>300}
h[a]       #=> 100

rehashドキュメントに含まれている例の興味深い動作に注目してください。k.hashハッシュは、キーの値を使用して物事を追跡しますk。キーとして配列があり、その配列を変更すると、配列のhash値も変更できます。その結果、ハッシュはまだその配列をキーとして持っていますが、ハッシュはその配列をキーとして見つけることができませんhash。古いhash値。しかし、あなたrehashがハッシュであれば、突然すべてのキーを再び見つけることができ、老化はなくなります. 非配列キーでも同様の問題が発生します。キーを変更する必要があるだけです。hash値が変更され、そのキーを含むハッシュが混乱し、迷子になるまで迷子にrehashなります。

Set クラスは内部的に Hash を使用してメンバーを格納し、メンバーはハッシュのキーとして使用されます。そのため、メンバーを変更すると、セットが混乱します。Set にrehash方法があれば、Set の頭を逆さまに叩いて問題を回避することができますrehash。残念ながら、Set にはそのようなメソッドはありません。ただし、次の場所で独自のモンキー パッチを適用できます。

class Set
  def rehash
    @hash.rehash
  end
end

次に、キーを変更rehashし、セットを呼び出すと、delete(および などの他のさまざまなメソッドmember?) が適切に機能します。

于 2012-04-28T07:34:36.453 に答える