From: "Y. NOBUOKA" Date: 2011-01-27T19:14:33+09:00 Subject: [ruby-dev:43126] Re: Hashのイテレーション中の新規キー追加 信岡です。 > ここで戻って 1.9 向けのパッチ後の挙動がどうあるべきかを考えると、現在の 1.9 の仕様として、 > * 1.9 の Hash#each は挿入順で評価される(仕様) > があるので、あるべき挙動は、 > * each 中のハッシュへの要素追加を常に許す > * 追加した要素は最後に評価する > となるべきだ、というのは割と合意の取れる線かなぁと思います。 そうですね、私も上記の仕様になるべきかなぁと思います。 > 近永さんのkeys.eachや成瀬さんの > > class Hash > def each > keys = hash.keys > keys.each do |key| > yield(key, hash[key]) if hash.key?(key) > end > end > end > > などはeach評価中に追加した要素は同じeach中に評価されません。 > 基本的にはeach中に評価される要素とeach回数を制御する要素を > 分離していることによる解決方法だと言えると思います。 ちょっと邪道かもしれませんが、[]= メソッドをオーバーライドしてイテレーション中に追加された要素もキーの配列に追加するようにすれば期待通りの動作になると思います。 Ruby 1.9 trunk 向けのパッチを考えられるとのことですが、現状の Ruby (1.9.2 以下) において期待する動作をさせたい場合は次のようなコードを使えばとりあえずは動くはずです。 # Hash を拡張するモジュールの定義 module MyHashExtension def []=( key, val ) super( key, val ) if @keys and not self.has_key? key then @keys << key end end def each @keys = self.keys @keys.each do |k| yield k, self[k] if self.has_key? k end @keys = nil end alias :each_pair :each end # 以下サンプル h = {}.extend MyHashExtension h[1] = 2 # イテレーション中に追加された要素もイテレーションに組み込まれる h.each do |k,v| h[k+1] = v*2 if v < 10000000000 $stdout << "#{k}: #{v}\n" end -- 信岡 ゆう (NOBUOKA Yu) http://www.vividcode.info/