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 }
当然、以下のコードもSyntax Errorです
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)