【一人 Ruby Advent Calendar 2017】ブロックについていろいろ【4日目】

一人 Ruby Advent Calendar 2017 4日目の記事になります。
ちょっと遅れてしまい申し訳ありません。
さて、今回は Ruby といえばブロック、ブロックといえば Ruby ぐらいに Ruby の特徴的な機能の1つであるブロックについて書いてみます。

ブロックとは

Ruby ではメソッドに対して『任意の処理』を渡す手段とし『ブロック引数』という特別な引数があります。
メソッドでブロック引数を受け取る場合、最期の変数名に & を追加して受け取ることができます。

# a, b は通常の引数
# &block はブロック引数
def func a, b, &block
end

また、ブロック引数は必ず Proc オブジェクトになります。

ブロック引数の渡し方

ブロック引数は通常 do 〜 end もしくは {} でブロックした『処理』を『Procオブジェクトとして』渡します。

def func &block
    # block は Proc オブジェクト
    p block
    # => #<Proc:0x0000000000b55888@/tmp/vtKnROY/351:10>

    # Proc#call で処理を呼び出す
    block.call 1, 2
end


# func 内の block.call で {} 内の処理が呼び出される
# a, b, は block.call の引数
p func { |a, b| a + b }
# => 3

# 以下のような渡し方でもおk
# func do |a, b|
#  a + b
# end

このようにブロック引数を使用することでコールバック関数のような処理を実現することができます。
以下のように Proc オブジェクトを引数として渡すこともできますが、

def func block
    # Proc#call で処理を呼び出す
    block.call 1, 2
end

# proc {} をコールバック関数として渡しているが…
p func(proc { |a, b| a + b })
# => 3

Ruby ではブロックを利用したほうが簡潔に記述することができますね(まあ proc 自体も引数としてブロックを受け取っているのですが。
また、ブロック引数は1つだけしか受け取ることができません。

# Error
def func &block1, &block2
    #
end

ブロックの使用例

Ruby ではブロックがあちこちで使われていますが、例えば次のようなイテレーションメソッドでも利用されています。

# 配列の要素を選択する処理をブロックで記述する
p [1, 2, 3, 4, 5, 6].select { |it|
    # 偶数のみ選択する
    it % 2 == 0
}
# => [2, 4, 6]

# 配列の要素を変更する処理をブロックで記述する
p [1, 2, 3, 4, 5, 6].map { |it|
    # 要素を2倍にする
    it + it
}
# => [2, 4, 6, 8, 10, 12]

このようにしてメソッドに対して簡単に処理を渡すことができます。

まとめ

  • Ruby のブロックは処理をメソッドに渡すための機能
  • メソッドでコールバック引数みたいなのを受け取りたい場合はブロック引数として受け取ると Ruby らしい
    Ruby をやり始めるとまず目に付くのがブロックの記述だと思いますが、実際はそこまで難しい概念ではないと思うので Ruby を学ぶのであればまず最初に覚えておきたいですね。