Rubyの条件式としての範囲式「..」と「...」の違いを理解する
はじめに
Rubyのリファレンスマニュアルを読んでいて、条件式としての範囲式の部分で「..」と「...」の違い
を理解するのに時間がかかったので学んだことをまとめます。
間違っている点などあればコメントを頂ければありがたいです。
前提
- ruby 3.1.2
そもそも範囲式とは?
rubyには範囲式というものが用意されています。
このとき「..」と「...」のどちらを用いるかで終点を含むか含まないかの違いがあります。
例えば、以下のように書くとそれぞれ1~20、1~19の範囲オブジェクトを生成できます。
1 .. 20 # 1~20の範囲の範囲オブジェクトを生成
1 ... 20 # 1~19の範囲の範囲オブジェクトを生成
条件式としての範囲式
上で紹介した範囲式はif文の条件式などで用いることが可能です。
この条件式としての範囲式をフリップフロップとも呼ぶらしいです。
以下のような使い方をすることができます。
5.times{|n|
if (n==2)..(n==3)
p n
end
}
#=> 2
# 3
5.times{|n|
if (n==2)...(n==3)
p n
end
}
#=> 2
# 3
挙動が変わるコードの例
実は条件式としての範囲式では「..」と「...」で挙動が若干違います。
まずはどんな違いが発生するのか見てみましょう。
5.times{|n|
if (n==2)..(n==2)
p n
end
}
#=> 2
5.times{|n|
if (n==2)...(n==2)
p n
end
}
#=> 2
# 3
# 4
直感的にはnの範囲が2~2のときにtrueになりそうなので、「...」を用いた場合の実行結果が
直感に反するように感じられます。
「..」を使った場合
リファレンスでは以下のように説明されています。
「..」の場合:
1.初期状態では式1だけを評価し、式1が真を返すまでは false を返します。
2.式1が真を返すと true を返します。式2が真なら初期状態に戻ります。
3.この後は式2だけを評価し、式2が真を返すまで true を返します。
4.式2が真を返すと true を返したあと、初期状態に戻ります。
この説明と照らし合わせながら先程の挙動が変わるコードの動きを追ってみましょう。
以下にそれを表として示します。
nの値 | 式1 | 式2 | 条件式の値 |
---|---|---|---|
0 | 偽 | 評価されない | false |
1 | 偽 | 評価されない | false |
2 | 真 | 真 | true |
3 | 偽 | 評価されない | false |
4 | 偽 | 評価されない | false |
ここで注目してほしいのは、先程の説明の3., 4.の部分は今回は実行されていないという点です。
なぜなら2.で式2が真なので初期状態(1.)に戻っているためです。
「...」を使った場合
リファレンスでは以下のように説明されています。
「...」の場合:
1.初期状態では式1だけを評価し、式1が真を返すまでは false を返します。
2.式1が真を返すと true を返します。
3.この後は式2だけを評価し、式2が真を返すまで true を返します。
4.式2が真を返すと true を返したあと、初期状態に戻ります。
実は「..」と異なるのは2.の「式2が真なら初期状態に戻ります。」が無いということだけなんです。
これを頭の片隅に置きつつ、同様にコードの動きを追ってみましょう。
nの値 | 式1 | 式2 | 条件式の値 |
---|---|---|---|
0 | 偽 | 評価されない | false |
1 | 偽 | 評価されない | false |
2 | 真 | 評価されない | true |
3 | 評価されない | 偽 | true |
4 | 評価されない | 偽 | true |
動きを見てみると、n=2のときに式2が評価されないことが分かります。
「..」のときと違ってn=2のときに初期状態に戻っていないんです。
そのためn=2以降、つまりn=3, 4のときには説明の3.の部分が実行されています。
したがって条件式としてはtrueが返されるわけですね。
まとめ
「..」と「...」の違いをまとめると
式1がtrueになったときに、式2を評価して初期状態に戻すか、戻さないか
ということになります。
すごい細かい違いなのでこれが原因でエラーになるみたいなことがあるのかは不明ですが
リファレンス読んでいて、すんなりと理解できなかった人の助けになれば幸いです。
Discussion