Rubyのブロック構文についてかく。
他メソッドにコード断片を渡すことで、そのメソッド内で処理を実行させることができる構文。
do..end
または{}
を使って定義する。ブロックは引数を受け取ることができる。
# do..end
[1...3].each do |number|
puts number
end
# {}
[1...3].each { |number| puts number}
ブロックを受け取るメソッドを定義することもできる。
def greet
puts "Hi"
# 渡されたブロックを実行
yield if block_given? # block_given?はブロックが渡されたかどうかを判定するメソッド
puts "Bye"
end
# ブロックを渡す
greet do
puts "Hello"
end
# 出力:
# Hi
# Hello
# Bye
また、ブロックをメソッドの引数として渡すこともできる。その場合は&
を使って渡す。
def greet(&block)
puts "Hi"
block.call if block
puts "Bye"
end
greet do
puts "Hello"
end
# 出力:
# Hi
# Hello
# Bye
to_procメソッドを持つオブジェクトもブロックとして渡すことができる。
class Greeting
def to_proc
Proc.new { |name| puts "Hello, #{name}!" }
end
end
%w(Alice Bob Charlie).each(&Greeting.new)
# 出力:
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!
ブロックはProcやlamdaと同じくクロージャーである。
def count(&block)
puts block.call
return block
end
x = 0
b = count do
x + 1
end # =>1
x += 10
puts b.call # =>11
do..end
と{}
は結合度が異なり、{}
の方が結合度が高い。
array = [1, 2, 3]
# 結合度が低いのでブロックがmapに渡される前に評価される(ppに渡されてしまう)
pp array.map do |x|
x * 2
end
# 出力:
#<Enumerator: ...>
# 結合度が高いのでブロックがmapに渡されて評価される
pp array.map { |x| x * 2 } # => [2, 4, 6]
ブロックの中で単一のメソッドを呼び出す場合に限り、ブロックをシンボルで置き換えることができる。シンボルにはto_procメソッド(Symbol#to_proc)が実装されている。
# 通常の形
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select { |n| n.even? }
p even_numbers.inspect # => [2, 4]
# シンボルを使った形
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select(&:even?)
p even_numbers.inspect # => [2, 4]
# 自作のメソッドでシンボルを使った形
module Numbers
def my_even?
self % 2 == 0
end
end
class Integer
include Numbers
end
puts [1, 2, 3].select(&:my_even?) # => [2]
do..end
と{}
の使い分けとしては、次のような考え方がある。
do..end
{}
{}
do..end
コーディング規約に依る部分もあるので、あくまで一例として考えておきたい。
関連書籍