From: Kimura Yoshihiro Date: 2009-07-30T20:03:20+09:00 Subject: [ruby-dev:38962] [Feature #1840] Hash に要素追加して self を返すメソッド Feature #1840: Hash に要素追加して self を返すメソッド http://redmine.ruby-lang.org/issues/show/1840 起票者: Kimura Yoshihiro ステータス: Open, 優先度: Low 配列から Hash を作るときなどに、よく a.inject({}) {|h, x| h[x] = ...; h } みたいにするのはもはやイディオムと言ってよいと思いますが(*1)、 「; h」の部分が少々オマジナイっぽいのが気になっていました。 気になったついでに検索してみたら他にもそのような意見を目にしたので(*2)、 Hash に「要素追加して self を返す」ようなメソッドを追加することを 提案してみます。 いくつか選択肢を考えてみました: 1. Hash#store が self を返す 仕様が変わるので互換性の面で問題が出るかもしれません。 しかも h.store(x, ...) だと字面が多くなるという。 h.store(*v) のように書けるケースなら短かくなるかもしれません。 2. Array#insert に合わせて Hash#insert を追加する []=, store, insert と似たようなのが複数あるのは、初心者にとって 単にわかりにくいだけかもしれません。 3. Hash#<< を追加する ML を検索したら、[ruby-list:44686] でまつもとさんが、 >ここからの類推で言うとHashに「<<」演算子を追加したとしても、 > > a << b > >は a.store(*b) と期待されてしまうのではないでしょうか。少な >くとも私は最初そう思いました。 とおっしゃっているのを見つけました。 これを使うと、h << [k, v] << ... して h.assoc(k) した結果が Array とまったく同じになる、という美しさがあります。 また、キーと値の配列に対して a.inject({}, &:<<) で済んでしまうのは うれしいかもしれません。 4. Enumerable#each_with_object を使う それはそれでありかもしれません。個人的には、each のついでに… と いう名前を object が欲しい局面で使うのはなんとなく違和感があります。 似た名前の each_with_index が self を返すからかもしれません。 5. その他 このイディオムを使う局面は、キーの集合を別の集合にマッピングした新 しい Hash が欲しい、というケースだと思われます。それを 「キーの集合に {}を inject する」と表現することが、そもそも直感的で ないとも考えられます。 # 「zip して flatten して * 展開」というのと同様に Hash 生成用のクラスメソッドとして、例えばこんなのがあると便利かも しれません(*3)。 def Hash.map(enum) if block_given? enum.inject({}) {|h, k| h[k] = yield k; h } else enum.inject({}) {|h, v| h.store(*v); h } end end # ex Hash.map(string_keys, &:size) # 文字列 => サイズ Hash.map(values.each_with_index) # 値 => インデクス Hash.map(other_hash.map {|k, v| [v, k] }) # 値 => キー 他の方の意見やアイデアも聞いてみたいです。 (*1) Google Code Search で「lang:"ruby" inject\({}\).+;\s*\w+\s*}」 とやったら 2,000 件ヒットしました。 (*2) http://subtech.g.hatena.ne.jp/cho45/20071128/1196239612 とか http://d.hatena.ne.jp/takkan_m/20071212/1197466247 とか http://wota.jp/ac/?date=20081025#p09 とか 某chとか (*3) 過去、某chに似たようなのを書きこんだのは私です ---------------------------------------- http://redmine.ruby-lang.org