1.8.6 で 1.8.7 な inject を使う

Ruby 1.8.7のinjectはSymbolを引数に取ることができる。つまり、合計は「inject{|s,x| s+x}」から「inject(:+)」でよくなった。

1+2+3+4                         # => 10
(1..4).inject(:+)               # => 10

1*2*3*4                         # => 24
(1..4).inject(:*)               # => 24
今はinjectよりもtapだよね〜 - http://rubikitch.com/に移転しました

を見て、Symbol を引数に取れる inject がいいなぁと思ったので
1.8.6 でもそれっぽく動作するように inject を書き換えてみた。

module Enumerable
  alias_method :_inject, :inject
  
  def inject(arg)
    if arg.class == Symbol
      return nil if self.to_a.size == 0
      case arg
      when :+ ; return _inject(0) {|r, i| r += i; r }
      when :- ; return _inject(0) {|r, i| r -= i; r }
      when :* ; return _inject(self.to_a.shift) {|r, i| r *= i; r }
      when :/ ; return _inject(self.to_a.shift) {|r, i| r /= i; r }
      end
      self
    else
      _inject
    end
  end
end

p (1..4).inject(:+)     #=> 10
p (1..4).inject(:-)     #=> -10
p [50, 2, 5].inject(:*) #=> 500
p [50, 2, 5].inject(:/) #=> 5


これでいいのかな?
なんかまずいところがあるかもしれないけど、とりあえず意図した通りに動くからまあいいか。

追記

injectは引数を省略できるし、四則演算以外のシンボルにも対応している。よーく見てみれば__send__を使えばいいことに気付くだろう。

module Enumerable
  alias_method :_inject, :inject

  def inject(arg=nil, &block)
    if arg.class == Symbol
      _inject {|s,x| s.__send__(arg, x)}
    elsif arg
      _inject(arg, &block)
    else
      _inject(&block)
    end
  end
end

(1..4).inject(:+)               # => 10
(1..4).inject(:-)               # => -8
[50, 2, 5].inject(:*)           # => 500
[50, 2, 5].inject(:/)           # => 5
(1..4).inject{|s,x| s+x}        # => 10
(1..4).inject(0){|s,x| s+x}     # => 10
そこで__send__ですよ - http://rubikitch.com/に移転しました

おー。なるほど __send__ かぁ。
勉強になります!
そして四則演算以外のシンボルも使えるのっていうのは面白い。

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

こんなのもできる。
あんまりいい例じゃないけど。