割とどうでもいい話。
数字を文字列化した配列が欲しいとき…
number_strings = (1..10).to_a.map{|n| n.to_s }
p number_strings
#=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
number_strings = []
[*1..10].each{|n| number_strings << n }
p number_strings
#=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
とかやってませんか?
配列を作ってすぐにmap
とかeach
とかselect
するような場合、同じメソッドがRange
にもあるので、to_a
は要らない。
「Array
とRange
の両方にあるメソッド」は次のような式を実行することで列挙できる。
p ((1..10).to_a.methods & (1..10).methods) - Object.new.methods
#=> [:to_a, :first, :last, :each, :reverse_each, :size, :find_index, :sort, :collect, :map, :select, :reject, :zip, :include?, :count, :cycle, :take, :take_while, :drop, :drop_while, :bsearch, :entries, :sort_by, :grep, :find, :detect, :find_all, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :each_entry, :each_slice, :each_cons, :each_with_object, :chunk, :slice_before, :lazy]
逆に、Range
のままでは不都合で、一度Array
にしなければならないメソッドを調べるには、以下のようにする。
p (1..10).to_a.methods - (1..10).methods
#=> [:to_ary, :[], :[]=, :at, :fetch, :concat, :<<, :push, :pop, :shift, :unshift, :insert, :each_index, :length, :empty?, :index, :rindex, :join, :reverse, :reverse!, :rotate, :rotate!, :sort!, :sort_by!, :collect!, :map!, :select!, :keep_if, :values_at, :delete, :delete_at, :delete_if, :reject!, :transpose, :replace, :clear, :fill, :slice, :slice!, :assoc, :rassoc, :+, :*, :-, :&, :|, :uniq, :uniq!, :compact, :compact!, :flatten, :flatten!, :shuffle!, :shuffle, :sample, :permutation, :combination, :repeated_permutation, :repeated_combination, :product, :pack]
「Array
にあってRange
にない」メソッドは配列を結合するメソッドとか破壊的な変更するメソッドが多いので、比較的どうでもいい。
最初の例はここまで縮められる。
p (1..10).map(&:to_s)
#=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
こんなこともできる。
p (1..10).select(&:odd?)
#=> [1, 3, 5, 7, 9]
require 'prime'
p (2..100).select(&:prime?)
#=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
どの例でも返り値はArray
である。
もちろんinject
もある。
p (1..10).inject{|acc, n| acc * n }
#=> 3628800
あとがき
一文につきたかだか5Byteとメソッドコールを一回減らせてもたいしてうれしくないひとが多いらしいので、書くひとがめんどくさくないのならば、結構どうでもいい。僕は1Byteでも減るとうれしい。
追伸
コスト? もちろん余計な変換はしない方が早いし、メモリの使用も少く済むに決まってるじゃないですか。ただし、そこまでけちるようならばそもそもRubyを選択する意義に乏しいので、ユーザーは表面的に見えるコードの簡潔さと、小手先の倹約ではないアルゴリズムそのものに集中するべきである、といふのが私の認識です。といふわけで、(要素数にもよるけど)私はto_a
するな! なんて主張をする気は一切ないのです。読みやすいコードが正義。
追伸 2
忘れてたけど[*1..10]
みたいに配列リテラルのオペランドにリテラルで作った範囲オブジェクトを展開するのってあまり欲しい機会なくね? ってことを書きたかったんだった。