コンテンツにスキップ

範囲 (Range)

Rangeは2つ値の区間を表します。通常、範囲は2つもしくは3つのドットからなる範囲リテラルによって生成されます。

  • x..y: 2つのドットは右の値を含む範囲を表します。つまりxy、およびその間のすべての値を含みます (数学的には閉区間 [x, y] です)。
  • x...y: 3つのドットは右の値を含まない範囲を表します。つまりxyまでの値すべてを含みます (数学的には左閉右開区間 [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..yRange.new(x, y) と等しく、 x...yRange.new(x, y, true) と等しいです。

左右の値は必ずしも同じ型である必要はありません。例えば true..1 は有効な範囲リテラルです。しかし、この場合2つの型は比較不能のため Enumerable のメソッド群がほとんど使えず、あまり便利な場面はありません。2つの型は少なくとも比較可能である方が好ましいです。

左の値として nil が与えられた範囲を begin-less 範囲、右の値として nil が与えられた範囲を end-less 範囲と呼びます。リテラルを書く上では nil は省略できます。つまり x..x から始まる end-less 範囲で、..xxで終わる 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 な範囲もリテラルとして有効ですが、あまり使いどころはないでしょう。