Ruby の yeild を理解してみる

例題

module Enumerable
  def sum
    if block_given?
      # @see http://www.ruby-lang.org/ja/man/html/Enumerable.html#inject
      inject(0) {|result, value| result + yield(value) }
    else
      inject(:+)
    end
  end
end

ary = [1, 5, 3]

# ブロックを追加しない場合
# 総和
p ary.sum            # => 9

# ブロックを追加した場合
# 二乗和
p ary.sum {|x| x * x}  # => 35

# 下記のように書き直せる
# ary.sum do |x|
#     x * x # => 35
# end

ary.sum の場合

  1. ary.sum にはブロックが渡されていない
  2. sum メソッドでブロックが渡されているかどうかの判定 if block_given? で偽になる
  3. inject(:+) で ary[1, 5, 3]が加算される
    (1 + 5 + 3 = 9)

ary.sum {|x| x * x} の場合

  1. ary.sum に {|x| x * x} ブロックが渡されているので、if block_given? で真になる
  2. inject(0) { ... } で ary 配列の 0番目から処理が始まる
  3. inject(0) { ... } ブロックに 1 が value に代入されて、yeild に引数として渡される
  4. yeild に渡された後、{|x| x * x} ブロックに渡されて、二乗される
    (1 * 1 = 1)
  5. 二乗された値は、inject(0) { ... } ブロックに戻り、result に代入される
    (result = 1)
  6. 次の配列の値 5 が value に代入された後に、yield を経て {|x| x * x} で二乗される
    (5 * 5 = 25)
  7. そして二乗された値は、value と合算される
    (value = value + 25 = 26)
  8. 6. からの処理と同様に配列の値 3 が渡され、同様の処理を得て、二乗和の 36 になる
    (value = value + 9 = 36)

簡単なまとめ

  1. yield はブロック呼び出しに使います。
    ⇒ yield を定義したメソッドの後に記述されたブロックを呼び出す。
  2. yield はブロックを評価した結果を返します。
    ⇒ 例文の場合、{|x| x * x} ブロック内で x * x を計算した結果を yield が戻り値として返す。
  3. ブロックの評価結果を使用するタイプです。
    ⇒ メソッド sum 内で yield に渡された引数は {|x| x * x} ブロックで処理され、その戻り値を引き続き sum メソッド内で処理を行う。
  4. 繰り返し処理が複数回ある場合、yield を利用することでブロックを入れ子にする必要がなくなり、メソッドチェインとして記述できるようになるメリットがある。