injectつづき

http://d.hatena.ne.jp/ha-tan/20060717
この実装は素敵だなぁ.素敵な記法は貪欲に吸収.


そうそう,Haskellには,foldlにつづきfoldl1というものがある.これは,injectでいうとこの初期値?が無いかたち.わかり易い例を示すと,こうなる.

Hugs.Base> foldl (+) 0 [1 .. 10]
55
Hugs.Base> foldl1 (+) [1 .. 10]
55

んじゃそれをRubyでやったらば?


とりあえずは何も考えず.

module Enumerable
  def inject1
    result = self[0]
    self.each_index do |i|
      i += 1
      break if i >= size
      result = yield(result, self[i])
    end
    result
  end
end

(1..10).to_a.inject1{|r, e| r += e}
=> 55

これは俗に言う,汚い実装.breakを使うと突然美しさが損なわれる気がする.


breakを使わない方向で考える.

module Enumerable
  def inject1
    result = (foo = self.dup).shift
    foo.each do |e|
      result = yield(result, e)
    end
    return result
  end
end

dupってアリ?でも,どうせならinjectを呼びたい.貪欲に.

module Enumerable
  def inject1
    result = (foo = self.dup).shift
    return foo.inject(result){|result, elem| yield(result, elem)}
  end
end

まだこっちのがカッコいい.


dupを使わない方向で考える.

module Enumerable
  def inject1
    result = self[0]
    for i in (1 ... self.size)
      result = yield(result, self[i])
    end
    return result
  end
end

for文.たぶん,Rubyやってて初めて書いた.あぁ,ってかこっちでもinject呼んでやれば良いじゃない.

module Enumerable
  def inject1
    self[1 ... size].inject(self[0]){|result, elem| yield(result, elem)}
  end
end

やった1行だ.

追記

プログラミング言語 Ruby リファレンスマニュアルを見る以上,

このモジュールのメソッドは全て each を用いて定義されてい

なければならないのかしらという強迫観念にかられ(そんなことはない!),かつid:ha-tanさんのまねごとをしつつ,eachを用い,[]など配列に依存するものを用いず,考える.

module Enumerable
  def inject1
    result = nil
    each do |elem|
      if result == nil
        result = elem
      else
        result = yield(result, elem)
      end
    end
    result
  end
end

ha-tanさんのと殆ど変わらない.てかこのifをどーにかできないものか.と思うと同時に,こんなことばかりしてるからレポートが進まないのだと己に言い聞かせながらも,まだ考える.だけどもうこれ以上考えられないんで,とりあえず今日は終わり.

追記2

b2oxさんのコメントより,せめてif-else-endに書き直しました.