ブロック / yield / block_given?
| 対応: | Ruby 1.8(2003) |
|---|
ブロックはメソッドに渡せる処理のかたまりです。『yield』でブロックを呼び出し、『block_given?』でブロックの有無を確認できます。
構文
# ブロック付きでメソッドを呼び出す(do...end 形式)
メソッド名 do |引数|
処理
end
# ブロック付きでメソッドを呼び出す({} 形式)
メソッド名 { |引数| 処理 }
# メソッド定義内でブロックを呼び出す
def メソッド名
yield # ブロックを実行する
yield(引数) # 引数を渡してブロックを実行する
end
# ブロックが渡されているか確認する
def メソッド名
if block_given?
yield
end
end
構文一覧
| 構文・メソッド | 概要 |
|---|---|
| do...end | 複数行のブロックを定義します。優先順位が低いためメソッド引数との組み合わせで使われます。 |
| { } | 1行のブロックを定義します。優先順位が高く、インラインで書く際に使われます。 |
| yield | メソッドに渡されたブロックを実行します。引数を渡すことができます。 |
| block_given? | メソッドにブロックが渡されているかどうかを真偽値で返します。 |
サンプルコード
『greet』は yield でブロックを呼び出すメソッドです。
block_yield.rb
def greet
puts "開始します。"
yield # ブロックを実行する
puts "終了しました。"
end
greet do
puts "こんにちは!"
end
# yield で引数を渡す
def double(num)
yield(num * 2)
end
double(5) { |n| puts "結果: #{n}" } # 結果: 10
# block_given? でブロックの有無を確認する
def optional_process(val)
if block_given?
yield(val)
else
puts "ブロックなし: #{val}"
end
end
optional_process(42) # ブロックなし: 42
optional_process(42) { |n| puts "ブロックあり: #{n}" } # ブロックあり: 42
# yield の戻り値を使う
def convert(text)
result = yield(text)
puts "変換後: #{result}"
end
convert("hello") { |s| s.upcase } # 変換後: HELLO
ruby block_yield.rb 開始します。 こんにちは! 終了しました。 結果: 10 ブロックなし: 42 ブロックあり: 42 変換後: HELLO
Procオブジェクトでブロックを受け取る
proc_example.rb
# &proc でブロックを明示的にオブジェクトとして受け取る
def run_twice(&block)
block.call
block.call
end
run_twice { puts "未来ガジェット!" }
# 未来ガジェット!
# 未来ガジェット!
# Proc.new または lambda でブロック相当のオブジェクトを作成する
greet_proc = Proc.new { |name| puts "こんにちは、#{name}!" }
greet_proc.call("岡部倫太郎") # こんにちは、岡部倫太郎!
# & でProcをブロックとして渡す
names = ["岡部倫太郎", "牧瀬紅莉栖", "椎名まゆり"]
names.each(&greet_proc)
# こんにちは、岡部倫太郎!
# こんにちは、牧瀬紅莉栖!
# こんにちは、椎名まゆり!
ruby proc_example.rb 未来ガジェット! 未来ガジェット! こんにちは、岡部倫太郎! こんにちは、岡部倫太郎! こんにちは、牧瀬紅莉栖! こんにちは、椎名まゆり!
よくあるミス1: block_given?未確認のyield
『block_given?』を確認せずに『yield』を呼ぶと、ブロックなしで呼ばれた場合に LocalJumpError が発生します。
block_mistake.rb
def risky_method yield end risky_method
ruby block_mistake.rb block_mistake.rb:2:in 'Object#risky_method': no block given (yield) (LocalJumpError) from block_mistake.rb:5:in '<main>'
よくあるミス2: block_given?で安全に処理
『block_given?』で確認すれば安全です。
block_ok.rb
def safe_method
if block_given?
yield
else
puts "ブロックが渡されていません"
end
end
safe_method
safe_method { puts "安全!" }
ruby block_ok.rb ブロックが渡されていません 安全!
よくあるミス3: do...endと{ }の優先順位
『do...end』と『{ }』は優先順位が異なるため、メソッドの引数と組み合わせると意図と異なる動作になります。メソッド引数との組み合わせでは『{ }』を使うと安全です。
block_map.rb
result = [1, 2, 3].map { |n| n * 2 }
puts result.inspect
ruby block_map.rb [2, 4, 6]
概要
ブロックはRubyの重要な機能で、メソッドに処理を後付けするための仕組みです。『each』や『map』などのイテレータもブロックを受け取るメソッドとして実装されています。
『do...end』と『{ }』はどちらもブロックを定義しますが、慣習として複数行は『do...end』、1行は『{ }』を使います。ブロックなしで『yield』を呼び出すと『LocalJumpError』が発生するため、ブロックが任意の場合は必ず『block_given?』で確認してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。