範囲オブジェクトを配列に

範囲オブジェクトと配列って似てるなぁと思いつつ、どうやって範囲オブジェクトを配列にしたらいいのか分からなかった。単にObject.to_aとやるか、Array(foo)とかやればいいだけらしかった。

irb(main):001:0> a = (1..5)
=> 1..5
irb(main):002:0> a.to_a
=> [1, 2, 3, 4, 5]
irb(main):003:0> a
=> 1..5
irb(main):004:0> Array(a)
=> [1, 2, 3, 4, 5]
irb(main):005:0> a.to_ary
NoMethodError: undefined method `to_ary' for 1..5:Range
        from (irb):5
        from :0
irb(main):006:0> 

to_aryは暗黙的にArrayに変換が必要なときに内部的に呼ばれるメソッドで、デフォルトでは未定義らしい。ふーん。

ともあれ、前に書いた写真取り込みスクリプトのオプション解析で、

if exp =~ /(\d)-(\d)/
        Range.new($1,$2).each{|n| 
          numbers << n.to_i        
        }

と書いてあったところは非常に冗長で、単純に

numbers = Range.new($1,$2).to_a

とかやればいいってことか。しかし、どのタイミングでstringをintegerにすればいいのか良く分からない。

numbers = Range.new($1,$2).to_a.to_i

とはできない。今の場合、どうせ配列の中身は全部stringなんだから、こう書けたっていいじゃないかーと思うけど、それは無茶なのか。かといって、to_aとした後にまたeachで回せっていうのも何か違うような気がする。$1.to_iという書き方ができるらしいので、そっちのほうがいいか。

ところで、(1..5).step(2)でブロックを渡せるけど、(1..5).step(2).to_aはエラーになる。

irb(main):011:0> (1..5).step(2).to_a
LocalJumpError: no block given
        from (irb):11:in `step'
        from (irb):11
        from :0

マニュアルを見ると、

step(s=1) {|item| ... } -> self

となっている。戻ってくるのがRangeとか、to_aメソッドのあるクラスのオブジェクトじゃないってことかな? selfってなんだ? ていうか、ブロックの前に来るものってなんだ?

(5分ほど「たのしいRuby」を読む)

ブロックの前に来るのは「ブロックつきメソッド」とか「ブロックつき呼び出し」と呼ばれる特殊なメソッド、という説明だった。なんだ、特殊なメソッドってことか。ブロックを受け付けるかどうかはメソッドごとに決まってるだけだったのか。じゃあ逆にRange#stepに相当するブロックなしのメソッドってあるのかな?

と思って、空のブロックを渡してみたらけど、うまくいかない。そうか、selfって文字通り自分自身が戻り値になるってことか。

irb(main):039:0> (1..5).step(2){}.to_a
=> [1, 2, 3, 4, 5]

うーん、例えば1から100までの範囲にある奇数のリストを範囲オブジェクトっぽく生成するのってどうするんだろうか。

irb(main):078:0> (1..100).to_a.reject!{|i| (i % 2) == 0}
=> [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 
35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 
67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]

で、できたけど、なんか違うよなぁ。