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)