範囲 (Range)¶
Rangeは2つ値の区間を表します。通常、範囲は2つもしくは3つのドットからなる範囲リテラルによって生成されます。
x..y
: 2つのドットは右の値を含む範囲を表します。つまりx
とy
、およびその間のすべての値を含みます (数学的には閉区間[x, y]
です)。x...y
: 3つのドットは右の値を含まない範囲を表します。つまりx
とy
までの値すべてを含みます (数学的には左閉右開区間[x, y)
です)。
(0..5).to_a # => [0, 1, 2, 3, 4, 5]
(0...5).to_a # => [0, 1, 2, 3, 4]
Note
範囲リテラルをレシーバにしてメソッド呼び出しを続けるような場合、範囲リテラルを括弧で囲う必要があります。0..5.to_a
のように括弧で囲わなかった場合、範囲リテラルよりもメソッド呼び出しの方が優先度が高いため 0..(5.to_a)
として解釈されます。
上記の「右の値を含む/含まない」記法の簡単な覚え方を紹介します。ドットが1つ多くついていると、それだけyが外に押し出されているのをイメージしてください。だから、そのとき範囲にyは含まれないのです。
明示的にコンストラクタを呼ぶ場合、x..y
は Range.new(x, y)
と等しく、 x...y
はRange.new(x, y, true)
と等しいです。
左右の値は必ずしも同じ型である必要はありません。例えば true..1
は有効な範囲リテラルです。しかし、この場合2つの型は比較不能のため Enumerable
のメソッド群がほとんど使えず、あまり便利な場面はありません。2つの型は少なくとも比較可能である方が好ましいです。
左の値として nil
が与えられた範囲を begin-less 範囲、右の値として nil
が与えられた範囲を end-less 範囲と呼びます。リテラルを書く上では nil
は省略できます。つまり x..
は x
から始まる end-less 範囲で、..x
はx
で終わる begin-less 範囲です。
numbers = [1, 10, 3, 4, 5, 8]
numbers.select(6..) # => [10, 8]
numbers.select(..6) # => [1, 3, 4, 5]
numbers[2..] = [3, 4, 5, 8]
numbers[..2] = [1, 10, 3]
..
や ...
のような begin-less かつ end-less な範囲もリテラルとして有効ですが、あまり使いどころはないでしょう。