ham-capのブログ

プログラミング学習の記録

【Ruby】eachの返り値について

eachメソッドの返り値?

Ruby Silverの勉強をしていて、以下のようなコードの実行結果についての設問があった。

(1..10).each.reverse_each.each do |i|
  puts i
end

実行結果は以下。

10
9
8
7
6
5
4
3
2
1

単純にeachで取り出したものをreverse_eachで逆順にして、それをeachで繰り返し処理するというだけなのだけれど、自分は最初のeachの段階でレシーバである範囲オブジェクトから1が取り出されていると考えてしまって混乱してしまった。

ポイントは、eachそのものの返り値は何かということを誤解していたことだと思う。

eachの返り値はレシーバ自体

結論から言うと、そういうことらしい。 試しにirbで以下のコードを実行してみる。

(1..10).each do |i|
end
=> 1..10  # <= レシーバが返ってきている

レシーバが配列の場合でも、

array = [1,3,5]
array.each do |n|
end
=> [1, 3, 5] # <= やっぱりレシーバが返ってくる

こうなる。

もう一度最初のコードを見てみると、初めに呼び出されたeachはブロックを渡されない状態で、先ほどirbで実行したのと同じように何の処理もせずにレシーバを返して次のreverse_eachに処理が移っていると考えられる。

reverse_eachもブロックついてないのになぜ逆順になるのか?

最初のコードを見てみると、reverse_eachにもブロックは渡されていないため、先ほどのeachの例に当てはめるとレシーバがそのまま順序も変わらずに次のeachに渡されそうなものだが、実際には2個目のeachによって取り出される値はしっかり逆順になっていた。

これについてはリファレンスマニュアルで言及があり、曰く

ブロックを省略した場合は、各要素を逆順に辿る Enumerator を返します。

とあるので、挙動としてはこれで正しいようだ。

これ、内部的にはreverseが呼ばれた後にeachが呼ばれてるってだけなんじゃないかと思わなくもない。ただの想像だけれど。

今回は以上です。