From: Urabe Shyouhei Date: 2010-11-27T19:37:21+09:00 Subject: [ruby-dev:42639] Re: Enumerable#categorize (2010/11/27 18:45), Tanaka Akira wrote: > こういうデータから、各要素の第一要素から第二要素、 > あるいは第二要素から第一要素へのハッシュを作るというのはありがちな話です。 ニーズは理解できます。 > 幸いにして第一要素から第二要素へのハッシュは Hash[ary] で作れるようになったので、 > 逆の第二要素から第一要素へのハッシュについて考えましょう。 > (同姓同名を考慮して、ハッシュの値は第一要素の配列としましょう) > > これを得るには現在、残念な事に、自分でループを書く必要があります。 この状況を改善するという選択肢はありませんか。つまりHash[ary].invertがもう少し 賢くなればいいんですよね? > 引数の 1, 0 が何を意味するかというと、enumerable の要素から > ハッシュのキー及び値を取り出す指定です。 > 具体的には ary の各要素に対し、[] メソッドを呼び出し、 > その引数に引き渡してキー/値を得ます。 > つまり、["matz", "Yukihiro Matsumoto"][1] として "Yukihiro Matsumoto" というキーを得て、 > ["matz", "Yukihiro Matsumoto"][0] として "matz" という値を得るわけです。 分かりにくすぎると思います。 > どうでしょうか。 これが存在すること自体には特に反対しませんが、ちょっと抽象的すぎて逆に使いづら いと感じます。最終的にこのアルゴリズムに落ちたとしても、ユーザーにはもう少し使 いやすいAPIを提供すべきでしょう。たとえば数を数える専用のメソッドを作るとか。 Index: hash.c =================================================================== --- hash.c (revision 29883) +++ hash.c (working copy) @@ -1706,6 +1706,17 @@ return ST_CONTINUE; } +static int +rb_hash_invert_with_block_i(VALUE key, VALUE value, VALUE hash) +{ + if (key == Qundef) return ST_CONTINUE; + if (rb_hash_has_key(hash, value)) { + key = rb_yield_values(3, value, rb_hash_aref(hash, value), key); + } + rb_hash_aset(hash, value, key); + return ST_CONTINUE; +} + /* * call-seq: * hsh.invert -> new_hash @@ -1716,6 +1727,11 @@ * h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 } * h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"} * + * Without a block given, entries with identical values are + * clobbered. No guarantee on which one shall be chosen. With a + * block on the other hand, you can choose which (or both). See + * Hash#update for how. + * */ static VALUE @@ -1723,7 +1739,12 @@ { VALUE h = rb_hash_new(); - rb_hash_foreach(hash, rb_hash_invert_i, h); + if (rb_block_given_p()) { + rb_hash_foreach(hash, rb_hash_invert_with_block_i, h); + } + else { + rb_hash_foreach(hash, rb_hash_invert_i, h); + } return h; }