久々にRubyを触っていて、色々調べることが多い。
この間見つけた10年前のstack overflowがなんとなく面白かったので適当に言及してみる。「Rubyで配列の全要素に繰り返し処理する正しい方法はなんですか?」
stackoverflow.com 上から二番目の回答が好きなので、適当に訳してみる。
多分「正しい」方法はないんじゃないかな。イテレート(反復)の方法はたくさんあって、それらは適材適所だからね。
each
は色んな場面で使えるよね。インデックスとかどうでもいいことが多いし。each_ with _index
は Hash#each に似てる。値とインデックスを扱える。each_index
はインデックスだけ。あんまり使わない。"length.times"と同じだね。map
はイテレートするもう一つの方法。配列を別の配列に変換したいときに便利。select
は配列の一部を抜き出してイテレートしたいときに使うやつ。inject
は全要素の和とか積を出すのに便利。あとはある一つの結果にまとめるようなとき。なんかたくさん覚えなきゃいけないっぽいけど、心配ない。別に全部覚えなくてもなんとかなる。ただ、複数の異なるやり方を覚えてそれを使い始めれば、コードがより綺麗になる。そしてそれは、Rubyをマスターする道のりの一つでもあるんだ。
どんな言語でもイテレート処理は微妙に違うので、こういうシンプルかつ適切なアドバイスができる人はすごくいいなあと思う。新しい言語を触るときにこういう記事があるととても助かる。(どうでもいいけど、injectよりreduceの方がしっくりくるんだけどな…と思ったらエイリアスされてるらしい。よかった)
ところで、この回答がされたのは2008年12月。当時のRubyの最新バージョンはいくつだろう。Release Noteを見てみると、どうやらRuby 1.9.1-preview1が最新で、普通に考えたらRuby 1.8.7-p72がプロダクションで使える実質最新版だったと思われる。
つまり、この回答にはRuby 1.9以降で追加されたメソッドが考慮されていない。Rubyに詳しい人ならそんなの当たり前だろという感じかもしれないが、僕みたいな未熟者はリファレンスを見ないと安心できないので、Arrayクラスと親のEnumerableクラスのメソッドを確認してみる。
プログラミング言語 Ruby リファレンスマニュアルのArrayとEnumerableのページについて、Ruby 1.8.7とRuby 2.6.0を比較してみる。2.6.0にのみ存在するメソッドは以下の通り。
# Arrayクラスのメソッド append push bsearch bsearch_index difference dig filter! select! to_s keep_if max min prepend unshift repeated_combination repeated_permutation rotate rotate! sample sort_by! sum to_h union # Enumerableクラスのメソッド chunk chunk_while collect_concat flat_map each_entry each_with_object filter grep_v lazy slice_after slice_before slice_when sum to_h uniq
この中で配列の全要素探索に関係しそうなのはeach_with_object、sum/max/minあたりだろうか。
each_with_objectはinjectに似ているけどチョットチガウ的な感じ。https://blog.arkency.com/inject-vs-each-with-object/にいい感じの解説が書いてあった。個人的にはExample 2がわかりやすいと思う。(Example 1はなんかこう、それmapでええんちゃうの…と思ったので)
sum/max/mixは、この辺使えば全探索はいらないよという感じ。シンプルに便利。特にsumについては、上の記事で「総和はinjectで出せるよ」と書いてあるものの、sumの方がinjectで足し合わせるより早いらしいので覚えておくべきだろう。というかむしろ、以前はわざわざ全要素探索して加算や比較しないと出せなかったのかな。C言語かよ。もともとRuby 1.8を使っていたけど全然覚えてないな…。
まあそんなわけで、Ruby 2.6.0で配列の全要素探索するときは以下の7個+3個くらいのメソッドをおさえておけばよさそうです。なんか足りてなかったら誰か教えてください。
全探索
each
:インデックスいらないときeach_with_index
:インデックスほしいときeach_index
:インデックスだけほしいときmap
:別の配列に変換したいときselect
:一部の要素だけ抜き出して全探索したいときinject
(reduce
でも可):全探索しながら結果を積み上げたいときeach_with_object
:初期値ありでmapみたいなことがしたいとき(※個人の意見です)
以下を使えば全探索が不要になるよ
sum
max
min
補足:バージョン間のメソッド一覧差分の出し方
リファレンス上でいい感じに見れるかと思ったけどなんか無理っぽかったしGithub上でdiffを見ようにもちょっと辛かった。ので、リファレンスの目次のメソッド一覧をコピペした上でこんな感じのことをした。Macです。
# 1.8.7のメソッド一覧をコピペ pbpaste | sed -e 's/ /\'$'\n/g' > rb_array_1.8.7 # 2.6.9のメソッド一覧をコピペ pbpaste | sed -e 's/ /\'$'\n/g' > rb_array_2.6.0 # 2.6.0にだけあるメソッドの一覧表示 diff rb_array_1.8.7 rb_array_2.6.0 |grep '>' |sed -e 's/> //g'
もっと簡単な差分の出し方があったら誰か教えてください。
蛇足
https://docs.ruby-lang.org/ja/2.6.0/class/Array.html のインスタンスメソッド一覧、アルファベット順に並んでると見せかけてunshift
だけ真ん中の方にあるのはなぜだろう。誰か教え(略)