injectが分からん。。→inject便利!

Enumerable.injectの意味がよく分からないので調べてみる。

まずリファレンス。

http://www.ruby-lang.org/ja/man/html/Enumerable.html#inject

inject([init]) {|result, item| ... } (ruby 1.7 feature)

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

なんのことやら。。
以下のコードで実験。

irb(main):017:0> [1,2,3].inject(0) {|r,i| puts "r:#{r}, i:#{i}"}
r:0, i:1
r:, i:2
r:, i:3
=> nil

最初のputsでnilを返しているから、次回以降のresultにnilが入るのだね。なるほど。


ではitemを返すようにすると?
→ 最後の要素を返すことになる。

irb(main):022:0> [1,2,3].inject(0) {|r,i| puts "r:#{r}, i:#{i}"; i}
r:0, i:1
r:1, i:2
r:2, i:3
=> 3


ではよくあるsumを書いてみよう。

irb(main):023:0> [1,2,3].inject(0) {|r,i| puts "r:#{r}, i:#{i}"; r+i}
r:0, i:1
r:1, i:2
r:3, i:3
=> 6


初期値なしの場合は最初の2要素がresultとitemに入るらしい。

irb(main):024:0> [0,1,2,3].inject {|r,i| puts "r:#{r}, i:#{i}"; r+i}
r:0, i:1
r:1, i:2
r:3, i:3
=> 6


しかしこれで何が嬉しいの?というのがよく分からん・・。と思っていたら、
id:ninjatottori:20080411#1207903182
を発見。
シンプルに書けるところがポイントかー。なるほど。


フィボナッチ数列を作ってみる。
each使用版。

r = [0,1]
(0..10).each{ r << r[-2] + r[-1] }
r
=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

inject使用版。確かに簡潔だな〜。

(0..10).inject([0,1]){|r,i| r << r[-2] + r[-1]}
=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]


joinも書けてしまう。

%w{ a b c }.inject{|r,i| r + "," + i}
=> "a,b,c"

[0,1,2,3].inject{|r,i| r.to_s + "," + i.to_s}
=> "0,1,2,3"


なんとなく分かった。

r = ○○
array.each{|i| r = (iが絡む式)}
r

の略記法なんだな。


あれ?ということはArrayの重複チェック(id:craftone:20080502#1209737230)もこっちの方が良いな〜。
injectすげー!

class Array
  def overlapped
    an_hash = Hash.new
    self.inject([]){|r,i|
      an_hash.include?(i) ? (r << i) : (an_hash[i] = nil; r)
    }
  end
end