31
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

三方分岐とUFO演算子

Posted at

プログラムを書いていると、ときどき「過去・現在・未来」とか「赤字・とんとん・黒字」のような3方向への分岐が必要になる場面があります。Rubyでスッキリ書くにはどうすればいいのでしょうか。

素直に書いてみる

何も考えずに書くと、次のようになります。

3way-simple.rb
if foo == bar
  # 等しい時
elsif foo > bar
  # fooのほうが大きい時
else
  # fooのほうが小さい時
end

もちろんこれでも動くのですが、同じ値での比較を2度も書いていて、あまりきれいではありません(この例ではfoobarだからどうということもないのですが、オブジェクトのプロパティなどややこしい式を二度も書くのは、正直めんどうです)。

UFO演算子

ここで、Rubyには「UFO演算子」とも呼ばれる<=>という演算子があります。これは、両辺の値を比較して「左が大きい」「等しい」「右が大きい」というごとに違う値を返します。で、よくそれぞれ「1」「0」「-1」を返すと言われるのですが、この演算子の最大の利用者であるComparableのマニュアルによれば「正の整数」「0」「負の整数」であればいいということで、決まった値は保証されていません。というわけで、UFO演算子を使って書き直すと

3way-ufo.rb
comp = (foo <=> bar)
if comp == 0
  # 等しい時
elsif comp > 0
  # fooのほうが大きい時
else
  # fooのほうが小さい時
end

多少はすっきりしましたが、一時変数を使わずにできないものでしょうか。

無限の彼方へ

Rubyのcaseは、単純な一致判定だけではなく、===演算子を使った比較をしてくれるので、いろんな操作をできます。「正か0か負か」ということで、こんなこともできなくはないです。

3way-infinity.rb
case foo <=> bar
when 0
  #等しい時
when 1.0..Float::INFINITY
  # fooのほうが大きい時
else
  # fooのほうが小さい時
end

UFO2機

ここで改めてマニュアルを見てみると、Numeric#<=>は(正常に比較できる場合)0、±1を返してくれるとのことでした。ということで、UFOを2つ連ねてしまえば、強制的に値を0、±1のどれかにしてしまうこともできます。

3way-ufo2.rb
case (foo <=> bar) <=> 0
when 0
  #等しい時
when 1
  # fooのほうが大きい時
else
  # fooのほうが小さい時
end

その他

今までのコードでは省略していましたが、比較不能な場合には<=>nilを返すこともあります。数値や文字列、日付といった値で問題となることはありませんが、nilが混入するなど型が揃わない場合や、Moduleなどのように全順序が決まらない場合もあるので気をつけましょう。

31
26
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?