見出し画像

PythonとRubyのrangeオブジェクト

急に重箱の隅を突くような話になってしまいますがPythonとRubyでrangeオブジェクトの動きが面白いなと思ったのでメモ。前回の投稿でも入れようかと思ったんですが長くなってしまったので別稿で。

PythonとRubyでrangeクラスのオブジェクトを作るとこんな感じ。

# Python3
sample = range(0, 10)

type(sample)
 => <class 'range'>
# Ruby
sample = 0..10

sample.class
 => Range

Pythonはrange関数を使うのに対してRubyは範囲演算子を使って0..10とするだけで0から10までのrangeオブジェクトを作ることができます。わーお簡単ですね。軽く感動しました。ちなみに上記だとPythonは0から9までですがRubyは0から10までです。Rubyで10を含めない場合は0...10と書きます。

で、例えばある値がrangeオブジェクトの範囲に収まっているかは以下のように判定します。

# Python3
sample = range(0, 10)

5 in sample
 => True
# Ruby
sample = 0..10

sample.include?(5)
 => true

Pythonはin演算子で要素が対象オブジェクトに含まれるか判定することができます。一方Rubyはinclude?メソッドを使います。

で、ここからが面白いなと思ったところ。int型の5ではなく、float型の5.5で試してみると以下のようになります。

# Python3
sample = range(0, 10)

5.5 in sample
 => False
# Ruby
sample = 0..10

sample.include?(5.5)
 => true

PythonはFalseを返すのに対してRubyはtrueを返します。同じrangeクラスでも少数の扱いは違うんですね。これはPython2系ではrange関数はリストを返していたことに拠るんじゃないかと。

# Python2.7
sample = range(0, 10)

type(sample)
 => <type 'list'>

sample
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


さらに。
Rubyでは範囲演算子を使って0以上10以下は1..10、0以上10未満は1...10と書けると触れましたが、include?メソッドを使うと以下のようになります。

# Ruby
a = 0..10
b = 0...10

a.include?(10)
 => true

b.include?(10)
 => false

b.include?(9.9999)
 => true

0以上など、上限を決めない範囲オブジェクトも作れます。

# Ruby
c = 0..Float::INFINITY

c.include?(99999999999999999999)
 => true

c.size
 => Infinity

Rubyでは無限大を Float::INFINITY で表すようです。間の :: は何だよって感じですが。これもまた今度勉強しようと思います。

さらにさらに。
で、もっと面白いと思ったのが0以上無限未満も作れます。

 #Ruby 
d = 0...Float::INFINITY  # 範囲演算子が ... であることに注意

d.size
 => Infinity

d.include?(Float::INFINITY)
 => false  # falseなのかよ!

sizeは無限大だけど無限は含まないってのは矛盾してませんかね?Rubyのソースコードを深く読んだりすればこの辺りの謎も解けるのかしら。

あ、あとぱっと調べたところRubyでは0より大きく10以下、とか0より大きく10未満みたいな開始の値を含まないrangeオブジェクトは範囲演算子で作れないみたいですね。

こんなんで作れるぜ、なんて方がいらしたら是非コメントください。

サポートいただけましたら椅子から転げ落ちて喜びます。