Edited at

Rubyの「ブロック」を理解する

Rubyのブロックを雰囲気で使ってる人に向けて、ブロックの使い方を整理する。


ブロックとはなにか

ブロックは「処理の集合体」です。無名関数とかラムダ式とかクロージャのようなものです。

例:

# { ... } がブロック

[1,2,3,4].map { |e| e + 1 }

# do ... endがブロック
[1,2,3,4].map do |e|
e + 1
end

ブロックは「メソッドに渡す」という形式でしか存在できません。なので、例えば以下のコードはSyntax Errorになります。

a = { |e| e + 1 }

a = do |e|
e + 1
end


ブロック付きメソッド呼び出し

メソッドがブロックを受け取る方法を説明します。


yield

1つ目のやりかたは、yieldを使うことです。

例:

def hoge

yield(100)
end

hoge { |e| e + 1 } # => 101

ちょっと分かりづらいですが、yieldがブロックを「起動」していると思ってください。yieldの引数がブロックの引数になります。

ちなみに、メソッドには ブロックとは別に 引数を渡すことができます。

def hoge(a)

puts a
yield(100)
end

hoge(1000) { |e| e + 1 } # => 101
# => "1000" が出力される


&block

2つ目のやりかたは、&block引数を使うことです。

def hoge(&block)

block.call(100)
end

hoge { |e| e + 1 } # => 101

このように&blockという引数を宣言すると、ブロックがblock変数に代入されます。そして、block.callメソッドを呼ぶと、ブロックの処理が実行されます。直感的ですね。

この場合も、ブロックとは別に引数を渡すことができます。

def hoge(a, &block)

block.call(a)
end

hoge(1000) { |e| e + 1 } # => 1001


ブロックオブジェクト(Proc)

「さっき『ブロックは変数に代入できない』って言ったじゃん!」と思った人がいると思います。

実はblockに入っているのはブロックではなく「Procオブジェクト」です。

Procオブジェクトは以下のように生成できます。

blk1 = Proc.new { |e| e + 1 }

blk1.call(100) # => 101

blk2 = proc { |e| e + 2 }
blk2.call(100) # => 102

Procはオブジェクトなので、変数に代入できます。

つまり、メソッドが&block変数でブロックを受けると、上のような処理が走ってブロックをオブジェクトにしているということですね。


Procからブロックへの変換

Procをブロックとしてメソッドに渡したい気持ちになることがあります。

plus_100 = proc { |e| e + 100 }

[1,2,3].map(plus_100) # => Error!

しかしこれはエラーになります。なぜならplus_100はProcオブジェクトであってブロックではないからです。

Procオブジェクトをブロックに変換するためには&を使います。

plus_100 = proc { |e| e + 100 }

[1,2,3].map(&plus_100) # => [101, 102, 103]

このように、&をProcオブジェクトの前につけることによって、Procオブジェクトを「展開」し、ブロックとしてメソッドに渡すことができます。


まとめ


  • ブロックとは処理の集合体である

  • ブロックは「メソッド呼び出しに渡す」という使い方しかできない

  • メソッドがブロックを受け取る方法は2つある



    • yield 命令


    • &block 引数



  • ブロックをオブジェクトに変換するとProcになる


    • block = Proc.new { |e| e + 1 }

    • block = proc { |e| e + 1 }




  • Procをブロックに変換するには&を使う


    • array.map(&block)