Open1

【ruby】範囲オブジェクトRangeのややこしさ

Kohki_TakatamaKohki_Takatama

小数のややこしさ

(1..2) == (1..2.0)   # => true
(1..2).eql? (1..2.0) # => false

eql?==の挙動が違う。まあこれは別言語・別クラスでもあることですね。


n = 2.5
p 1..n # => 1..2.5

(1..n).each { |i| p i }
# => 1
# => 2
# 終了

範囲は1 ~ 2.5だが、eachpしてみると12だけです。
=> stepのデフォルトが1になっています。
=> 健全ではないですが、実質的に「終点の丸め処理」と同じ挙動です。

n = 10.5

(1..n).step(0.5).each { |i| p i }
(1..n).step(5).each { |i| p i }
# 結果は以下


stepを変更することで、任意の小数・倍数も扱うことができます。

stepのややこしさ

Rangeクラスからは少し離れた話になりますが・・・

先ほど見たstepは「各ステップの大きさ」を表していました。
しかし、以下の例では「ループの上限」を指定しています。
「各ステップの大きさ」を表すには、引数を2つ与えなければなりません。

2.step(5){|n| p n}
# => 2
# => 3
# => 4
# => 5

1.1.step(1.5, 0.1) {|n| p n}
# => 1.1
# => 1.2
# => 1.3
# => 1.4
# => 1.5

この違いは「クラスの違い」です。

Rangeクラスに使うときは「ステップの大きさ」のみを指定できます。

https://docs.ruby-lang.org/ja/latest/method/Range/i/=25.html

一方で、Numericクラスのstepが、この章で触れたものです。
NumericIntefer, Float, Rational, Complexなどの基底クラスです。)

https://docs.ruby-lang.org/ja/latest/method/Numeric/i/step.html