この投稿 http://qiita.com/items/da15035975b3369c0330 を見て,見た目は確かに範囲指定ですが,その実態はオブジェクト指向な Ruby ならではなものがあるのになぁ…と思い立ったので書いてみます.
この動作の実態は,Array クラスのインスタンスの []
メソッドに Range クラスのインスタンスを渡しているわけです.
alpha = ["a", "b", "c", "d", "e"]
p alpha.[](Range.new(2, 4)) #=> ["c", "d", "e"]
p alpha.[](2..4) #=> ["c", "d", "e"]
# Range.new(2, 4) と 2..4 は等価.
p alpha[2..4] #=> ["c", "d", "e"]
# .[](arg) と [arg] は等価.
# (前者はメソッド呼び出しであることを強調した感じ.)
p alpha[Range.new(2, 4)] #=> ["c", "d", "e"]
# 当然こうなる.
p alpha.[](Range.new(2, 4, true)) #=> ["c", "d"]
p alpha.[](2...4) #=> ["c", "d"]
# Range.new(2, 4, true) と 2...4 は等価.
# new の第 3 引数は exclude_end
# (デフォルトが false) である.
まず Ruby では「全てがオブジェクト」です.配列もオブジェクトです.その配列にアクセスするための添字の如く書いた 2..4
と 2...4
もオブジェクトです.
2..4
は,Range クラスのインスタンスです.インスタンスならコンストラクタで生成しないの?…となったとき,いちいちそう書かなくても楽に同等のものが書けるよー,という風に用意されているのがリテラルです.
Range クラスとは範囲を表現するためのものです.
なので,範囲を生成するために,そのパラメタとして始まりの数値,終わりの数値,**終わりの数値を終端から除外するの?**の 3 つを与えるのです.3 つ目は与えなければ勝手に false にされます (参考 : http://doc.ruby-lang.org/ja/1.9.3/class/Range.html の特異メソッド new の部分).
実際に見てみましょう.
p Range.new(2, 4) == (2..4) #=> true
p Range.new(2, 4, true) == (2...4) #=> true
# 「範囲」ならではなメソッドを持っている
p Range.new(2, 4).include? 3 #=> true
p Range.new(2, 4).include? 5 #=> false
p Range.new(2, 4).first #=> 2
p Range.new(2, 4).last #=> 4
次に,メソッド呼び出しについてです.
[]
は配列にアクセスするための組み込みの機能のようですが,少し発想を変える必要があります.
配列というオブジェクトにおいて,n 番目にアクセスするという機能を備えたメソッドが []
という名前で用意されているのです.そして,それを alpha[n]
のように書くことも出来るだけなのです.
あるオブジェクト x
の do_something
というメソッドを呼び出すときは x.do_something()
と記述するはずです.ならば,[]
というメソッドがあるならば x.[]()
で呼び出すのが道理.これがまかり通るのが Ruby です.(※setter メソッドの話や block の話はここでは考えません.)
信じられない?じゃあ以下を実行してみて下さい.
p ["a", "b", "c"].[](0) #=> "a"
# こんなことも出来ます
p 6.+(2)
p 6.-(2)
p 6.*(2)
p 6./(2)
p 6.%(2)
Array クラスの []
メソッドは,Range のインスタンスを受け取った時に特別な動作をするように出来ています (参考 : http://doc.ruby-lang.org/ja/1.9.3/class/Array.html のインスタンスメソッド self[range] の部分).
以上の事柄から,あたかも配列の範囲指定が出来るように見える書き方が出来ているわけです.
よって,結論としては,範囲指定の方法というよりも Range のリテラルにおいての *..*
と *...*
の違い,[]
メソッドに Range も渡せる Ruby ちゃんマジ可愛い! …と考える方が理解も深まり良いかと思います.