ブロック内での「yield」の仕組み
概要
以下のコードを理解するのが難しいです
module MyEnumerable
def my_select
new_array = []
each do |value|
new_array << value if yield(value)
end
new_array
end
end
class Week
include MyEnumerable
def initialize
@days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
end
def each
@days.each { |day| yield day }
end
end
week = Week.new
puts week.my_select { |day| day.start_with?("T") }
プログラムの結果出力
Tuesday
Thrusday
曖昧なコード
def each
@days.each { |day| yield day }
end
1.「yield」に指定されたブロックはありませんが、コードは引き続き機能します。
2.「yield」はブロックを与えるメソッド内で使用するものではないでしょうか?メソッド内の「yield」がブロックを呼び出すことは明らかです。
3.ブロック内の「yield」はどのように機能しますか?
「my_select」メソッドを次のようにリファクタリングしました
def my_select
new_array = []
each { |value| new_array << value if yield(value) }
new_array
end
end
「each」メソッドはブロックを生成します
each { |value| new_array << value if yield(value) }
def each
@days.each { |day| yield day }
end
「my_select」メソッドはブロックを生成します
week.my_select { |day| day.start_with?("T") }
def my_select
new_array = []
each { |value| new_array << value if yield(value) }
new_array
end
解決策
ブロックを直接渡して yield することはありません。囲んでいるメソッドを呼び出すときにブロックを渡すと、yield がそのブロックを呼び出します。例:
week = Week.new
week.each { |day| puts day }
# │
# └──── from `yield day'
はい、正確に。コード内の呼び出しは少し間接的です。 MyEnumerable とその my_select メソッドを含めて、それぞれのメソッドを呼び出します。
# ┌─────────────── this is your `each' method
# │
each do |value|
new_array << value if yield(value)
end
実行する |値| …end は探していたブロックです。 (my_select は独自のブロックにも値を生成するため、他のブロックも生成します)
特別なことは何もありません。 @days.each { |day| yield day } は @days の各要素に対して yield を呼び出します。静的な配列の内容を想定すると、各メソッドは次のように書き換えることができます。
def each
yield "Monday"
yield "Tuesday"
yield "Wednesday"
yield "Thursday"
yield "Friday"
end