計算の途中経過の保存

計算途中の値をとりあえず保存したり、そのための変数名を用意するするのって何だか面倒な気がするけど、Rubyでどうやったらそれをなくせるのかがよく分からない。与えられた数値のリストから平均値を求める計算は、Lispでは以下のように書ける、というか以前xyzzyLispを試したときに、こう書いた。

(defun average (x)
  (/ (apply #'+ x) (length x)))

(average '(5 3 1 8 10.0))
=> 5.4

これと似たことをRubyでやろうと思うと、

class Array
  def avg
    t = 0
    self.each{|i| t += i}
    t.to_f / self.size
  end
end

[1,2,3,4].avg
=> 2.5

というように配列の合計値をカウントアップするための変数tのようなものが必須になる。めんどくさい。と、そういう問題意識で再びArryaのマニュアルを見ていたら、どうもinjectメソッドというのがコードブロックの外側から初期値を突っ込めるメソッドとして存在していることが分かった。

class Array
  def avg
    self.inject(0){|t,i| t += i}.to_f / self.size
  end
end

と書けばいいようだ。相変わらずテンポラルな変数tが現れているけど、だいぶすっきりする。injectって最初にインジェクトする初期値は与えているけど、実際には次々と要素にブロック適用するときに、前の処理の結果を第1引数で受け継ぐという便利な仕様のようだ。なるほど! これはEnumerableモジュールの提供するメソッドで基本事項だったらしい。

よく分からないけど、selfは自明なので省略できるようだ。と、前から薄々思っていたけど、どうして省略できるのか、省略するとどう解釈されるのかがイマイチ分かってないので省略する気になれない。

class Array
  def avg
    inject(0){|t,i| t += i}.to_f / size
  end
end

レシーバとなるオブジェクトを省略すると、そのメソッドが呼ばれたレベルのオブジェクトがレシーバになるってことなのか。ということはまた日を改めて調べてみよう。