yield
ってやつがよくわからないので動かしてみた
基本形
def my_method
p 'my_method called'
res = 0
yield res # <- ブロック変数に格納される
p 'my_method done'
end
my_method do |res_from_yield|
p 'in block'
p res_from_yield # <- yieldに渡された値が入る
end
# 出力
# "my_method called"
# "in block"
# 0
# "my_method done"
実行順
-
my_method
を呼ぶ -
my_method
内、通常通り上から順番に実行される -
yield
がいたらblock内の処理を実行する
yield 値
とするとblockに値を渡す - blockの処理が終わったら
my_method
に帰ってくる
複数の引数を渡す
def my_method
p 'my_method called'
res = "res"
res2 = "res2"
yield res, res2
p 'my_method done'
end
my_method do |res, res2|
p 'in block'
p res, res2
end
# "my_method called"
# "in block"
# "res"
# "res2"
# "my_method done"
キーワード引数みたいのはハッシュにして渡すのがよさそう
def my_method
yield({ message: "Hello, World!", count: 3 })
end
my_method do |options|
puts "The message is: #{options[:message]}"
puts "The count is: #{options[:count]}"
end
# The message is: Hello, World!
# The count is: 3
スコープは普通の関数と同じ
goのクロージャのような挙動ではないので、以下のコードは0, 1, 2...
となるわけじゃない
def my_method
res = res || 0
yield res
res += 1
end
3.times do
my_method do |res_from_yield|
p res_from_yield
end
end
# 出力
# 0
# 0
# 0
当たり前だがインスタンス変数に置き換えてやればやりたいことはできる
def my_method
@res = @res || 0 # <- インスタンス変数に置き換える
yield @res
@res += 1
end
3.times do
my_method do |res_from_yield|
p res_from_yield
end
end
# 出力
# 0
# 1
# 2
用途を考えてみた
異なる種類のデータを同じ関数で柔軟に処理できる
やっぱり普通の関数でもよいきがしますね
def my_method(data)
if data.is_a? Array
data.each do |item|
yield item
end
elsif data.is_a? Hash
data.each do |key, value|
yield key, value
end
else
yield data
end
end
# 配列を処理するブロックを渡す
my_method([1, 2, 3]) do |item|
puts "item: #{item}"
end
# ハッシュを処理するブロックを渡す
my_method({ name: "Alice", age: 30 }) do |key, value|
puts "#{key}: #{value}"
end
# 単一の値を処理するブロックを渡す
my_method("Hello, World!") do |data|
puts data
end
感想
賢くないのでyield
は直感的に理解できなくて嫌いです
良い使い道があれば是非コメントしてください