質問
Ruby の Hash#each
はブロックに対して何を渡すか。
回答
うわー,これまたチョー初心者向けの質問やなあ,各要素のキーと値のペアに決まっとるやん。
だってほら,たとえば
food = {morning: 3, evening: 4}
food.each do |time, amount|
p [time, amount]
end
# => [:morning, 3]
# [:evening, 4]
ってなるやんか。
更問
その「キーと値のペア」ってどういうこと?
たとえば次のコードの結果はどうなる?
food = {morning: 3, evening: 4}
food.each do |x|
p x
end
回答
げっ,二つ渡されてるのにブロックパラメーターを一つしか書いてないときって,ええと・・・。
リファレンスマニュアルの「ブロックパラメータの挙動」に出てる
def foo
yield 1,2,3
end
foo{|v| p v} #=> 1
と同じようになるはずやから,
:morning
:evening
って表示されるやろな。
確認
ところが実際はこうなる:
food = {morning: 3, evening: 4}
food.each do |x|
p x
end
# => [:morning, 3]
# [:evening, 4]
驚愕
えっ,なんで???
解説
Hash#each
がブロックに渡しているのは,実は「キーと値のペアを長さ 2 の配列で表現した一つのオブジェクト」なわけ。
つまり,今の場合,[:morning, 3]
とか [:evening, 4]
という配列がブロックに渡されている。
ところがこれを
food = {morning: 3, evening: 4}
food.each do |time, amount|
# なんとかかんとか
end
のように,ブロックパラメーター二つで受けると,配列の第 0 要素が time
に入り,第 1 要素が amount
に入る。
ブロックに渡された値をブロックパラメーターがどのように受け取るか,その仕様の全貌はちょっと複雑なのだけど,このように単一の配列を複数のブロックパラメーターで受ける場合は配列が展開されることになっている。
納得
なるほどー。
蛇足
番号指定ブロックパラメーターを使った場合にどうなるかも見ておこう。
food = {morning: 3, evening: 4}
food.each{ p _1 }
# => [:morning, 3]
# [:evening, 4]
food.each{ p _1 ,_2 }
# => :morning
# 3
# :evening
# 4
food.each{ p _2 }
# => 3
# 4
_1
だけを使った場合,ブロックパラメーターは一つであるとみなされ,配列の展開は行われない。
_2
を使った場合,配列は展開される。