知らなかった Enumerable#reduce

Enumerable#reduce というメソッドの存在を最近知りました。

http://docs.ruby-lang.org/ja/2.2.0/class/Enumerable.html#I_INJECT

ruby使ってる人からしたら当たり前の事とは思いますが、 使ってみるとすごくきもちよかったのでメモしておきます。

例1

例えば下記のような配列があり、平均値を求めたい場合

nums = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

eachを使うと次のようになりますが、 (私は最初for文を使おうとしましたよ…?)

sum = 0
nums.each{|num| sum += num}
average = sum / nums.size

p average # => 8

reduceを使うと、次のようになります。

average = nums.reduce(0){|sum, num| sum + num} / nums.size
  • 前のブロックの実行結果を渡してくれるので、ブロック外で一時変数を作らなくてよいし、
  • 最終的な結果を値として返してくれるので、式の中に組み込めるのが、

よいですね。

下記のような書き方もできます。

average = nums.reduce(:+) / nums.size

初期値に最初の要素を使う場合は省略する事ができ、 計算の内容が「メソッドを呼ぶだけ」という場合は引数でメソッド名をシンボルで渡す事でブロックは省略できます。

例2

ブロックの結果に色々仕込むと、便利に使えますね。 次の例は、前の数との差分の合計という無意味な計算です。

diff, = nums[1...nums.size].reduce([0, nums[0]]){|(result, prev), num| [result + (num - prev), num]}

p diff # => 34

別名 inject

Enumerable#inject というのもありますが、名前が違うだけなんですね。 reduceは「一つにする」みたいなイメージですが、 「注入!」的な使い方をする場合はこちらを使うとコードのニュアンスが伝わりやすくなるという事でしょうかね。

次の例は、なんか劇的にわかりにくいですが、前の数との差分を空の配列に注入するという無意味な処理です。

diffs, = nums[1...nums.size].reduce([[], nums[0]]){|(result, prev), num| [result << (num - prev), num]}

p diffs # => [1, 0, 1, 1, 2, 3, 5, 8, 13]

Enumerable

eachを使った素敵メソッドをたくさん提供してくれるモジュールですね。 私には思いが及ばないものがたくさんありました。ちゃんと見ておこうと思います。

読み方 http://ejje.weblio.jp/content/enumerable