Hatena::ブログ(Diary)

Moderation is a fatal thing. Nothing succeeds like excess.

2009/01/14(Wed) 

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 でどのように書き変わってるかを図で示すとこんな↓感じ。

f:id:kenkitii:20090114150943p:image

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

sumiブロックの中身(sum + i)の実行結果
 0 1 0 + 1= 1
 1 2 1 + 2= 3
 3 3 3 + 3= 6
 6 4 6 + 4=10
10 510 + 5=15
15 615 + 6=21
21 721 + 7=28
28 828 + 8=36
36 936 + 9=45
451045 + 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厨になりましょう。

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。