nekoTheShadow’s diary

技術ブログとして始めたはずが、読書&愚痴ブログになりました(´・ω・`)

injectを使うときにはブロックの戻り値に気を付けましょう。

一昨日の記事で

(0..2).inject({}) do |hash,num|
  hash[num] = num
end

はうまくいかないが、

(0..2).inject({}) do |hash,num|
  hash[num] = num
  hash
end

はうまくいく、という内容を書きました。
今日はその補足です。

ここで便宜的に前者を「失敗した例」、後者を「成功した例」としておきましょう。

さて、両者を見比べたとき、その違いはブロックにあります。
「成功した例」ではブロックの戻り値がhash(Hash)になるのに対し、「失敗した例」ではnum(Fixnum)になっています。

hash = {}
num = 1
hash[num] = num
p (hash[num] = num).class #=> Fixnum
p hash.class #=> Hash

この違いが最終的な結果としてあらわれるのです。

ではるりまでinjectの項を見てみましょう。

2 回目以降のループでは、前のブロックの実行結果と self の次の要素を引数に順次ブロックを実行します。 そうして最後の要素まで繰り返し、最後のブロックの実行結果を返します。

つまり「成功した例」と「失敗した例」では2回目以降のループで渡される値が異なるわけです。
まず「成功した例」。

ループの回数 hash num ブロック ブロックの返り値
1回目 {} 0 hash[num] = num {0 => 0}
2回目 {0=>0} 1 hash[num] = num {0 => 0, 1 => 1}

もうお分かりだと思いますが、次は「失敗した例」。

ループの回数 hash num ブロック ブロックの返り値
1回目 {} 0 hash[num] = num 0
2回目 0 1 hash[num] = num ???


要するにinjectを使うときにはブロックの戻り値に気を付けてやる必要があります……という記事でした。
まあそのことを抜きにしてもinjectは便利です。
個人的には引数にSymbolを使えるところが素晴らしいと思います。

p (0..2).inject([],:push)
#=> [0,1,2]

これを突き詰めていけばもっと派手なことができそう。
目指せ黒魔術!