2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RubyAdvent Calendar 2021

Day 19

【初心者向け】Ruby の Hash#each はブロックに何を渡す?

Last updated at Posted at 2021-12-18

質問

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 を使った場合,配列は展開される。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?