Λάδι Βιώσας

http://profile.hatena.ne.jp/kenkitii/

ruby の inject をわかりやすく説明してみる

ruby の inject って慣れないと少し理解しづらいよなーと思ったので、極力わかりやすい説明をしてみるテスト。

わかりやすいかもしれない説明

さて、1 から 10 までの合計を求めるこんな↓コードがあった場合

sum = 0
(1..10).each {|i| sum = sum + i }
p sum # => 55

inject を使ってこのよう↓に書けます。

p (1..10).inject(0) {|sum, i| sum + i }

each と inject でどのように書き変わってるかを図で示すとこんな↓感じ。

injectの引数 0 は、ブロックローカルな sum 変数の初期値になってます。で、ブロックの実行結果の値が sum に代入されて、2回目以降のループを実行します。ループしている間の、各変数とブロックの中身はこんな↓感じ。

sum i ブロックの中身(sum + i) の実行結果
 0  1  0 + 1 = 1
 1  2  1 + 2 = 3
 3  3  3 + 3 = 6
 6  4  6 + 4 =10
10  5 10 + 5 =15
15  6 15 + 6 =21
21  7 21 + 7 =28
28  8 28 + 8 =36
36  9 36 + 9 =45
45 10 45 + 10 =55

最後に実行されたブロックの結果が、inject の戻り値となります。

よくあるパターン

んじゃ、inject で書き換える事ができる他のパターンを見てみます

フィボナッチ数列を求める

こんな↓コードがあった場合

fib = [1, 1]
(0..10).each {|i| fib << fib[i] + fib[i+1]}
p fib # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]

inject を使ってこのよう↓に書けます。

p (0..10).inject([1, 1]) {|fib, i| fib << fib[i] + fib[i+1] }
あるデータの度数分布を求める

こんな↓コードがあった場合

data = [:A, :B, :A, :C, :E, :A, :D, :B, :B, :C, :E]
h = Hash.new(0)
data.each {|key| h[key] += 1 }
p h # => {:A=>3, :B=>3, :C=>2, :E=>2, :D=>1}

inject を使ってこのよう↓に書けます。

data = [:A, :B, :A, :C, :E, :A, :D, :B, :B, :C, :E]
p data.inject(Hash.new(0)) {|h, key| h[key] += 1; h }

上の例の場合、ブロックの実行結果でハッシュ(h)を返したいので、セミコロンで区切って h を返しています。

まとめ

どの例でも、ループで計算した結果を保持しとく変数が、inject では、ブロックローカルな変数で済んでるのでスッキリした感じがしますね。そんなわけで、皆で inject厨になりましょう。