Help us understand the problem. What is going on with this article?

Ruby の範囲指定の正体

More than 5 years have passed since last update.

この投稿 http://qiita.com/items/da15035975b3369c0330 を見て,見た目は確かに範囲指定ですが,その実態はオブジェクト指向な Ruby ならではなものがあるのになぁ…と思い立ったので書いてみます.

この動作の実態は,Array クラスのインスタンスの [] メソッドに Range クラスのインスタンスを渡しているわけです.

array_range.rb
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..42...4 もオブジェクトです.

2..4 は,Range クラスのインスタンスです.インスタンスならコンストラクタで生成しないの?…となったとき,いちいちそう書かなくても楽に同等のものが書けるよー,という風に用意されているのがリテラルです.

Range クラスとは範囲を表現するためのものです.

なので,範囲を生成するために,そのパラメタとして始まりの数値終わりの数値終わりの数値を終端から除外するの?の 3 つを与えるのです.3 つ目は与えなければ勝手に false にされます (参考 : http://doc.ruby-lang.org/ja/1.9.3/class/Range.html の特異メソッド new の部分).

実際に見てみましょう.

range.rb
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] のように書くことも出来るだけなのです.

あるオブジェクト xdo_something というメソッドを呼び出すときは x.do_something() と記述するはずです.ならば,[] というメソッドがあるならば x.[]() で呼び出すのが道理.これがまかり通るのが Ruby です.(※setter メソッドの話や block の話はここでは考えません.)

信じられない?じゃあ以下を実行してみて下さい.

method.rb
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 ちゃんマジ可愛い! …と考える方が理解も深まり良いかと思います.

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away