久しぶりにRubyを書くと上段のような書き方をしてハマることが多い。Array#inject でHash.new([])をinject試みて失敗する。またハマってしまった。以前はhadoop の reduce で injectを使ったreducer 書いていてハマッてる。ハマり記録みて愕然とした。同じ失敗をまた繰り返している。
#!/usr/bin/env ruby ## ruby 1.8.7 (2009-06-12 patchlevel 174) [i386-mswin32] で実験 (1..3).inject(Hash.new([])){|hash,item| hash["some_key"].push item ;hash } #=> {} 動かない (1..3).inject({}){|hash,item| hash["some_key"]=hash.fetch("some_key",[]).push item ;hash } #=> {"some_key"=>[1, 2, 3] }
self[key] とHash#key(val) Hash#fetch が返すのは値であって、参照渡しでない。
Hash#key の戻り値は参照渡しでなく、値渡しなのだろうと思う。fetchがデフォルト値を返す用になっていることを考えれば参照で戻ってくることは考えにくい。Hashがその値を管理するテーブルを持っているから、値を更新するとき Hash#=で更新しないとダメなんだろう。もしくは、Array#pushの実装に由来するのかもしれない。配列の場合だけが特別なのだろうか。
ということで、 hashに入れた配列の値を更新するとき、面倒でも
hash["some_key"]=hash["some_key"].push item #再代入
と書いて、ハッシュのテーブルをちゃんと更新してあげないとダメみたい。この辺がまだよく解らない
分かっているつもりなんだけど、ついつい忘れてハマっちゃうんだよね。