Ruby で 1 < 2 < 3 みたいな比較演算を書けるようにする試み
TL;DR
華麗に失敗した。
発端
こんなモチベーションなので実用は目的にしていない。
何番煎じかもわからないけど、考えてみたかったので考えてみた。
問題をシンプルにする
仕様を絞る。
- 対象クラスを絞って
Integer
だけにする - 境界値を含まない場合だけを考える(
<
,>
だけ) ()
による演算の優先順位変更とかも考えない
アイデア
「左から順番に比較して、その結果を boolean じゃなく以前の 比較結果を保持するいい感じのオブジェクト で返せば良いのでは?」
左から順番に比較していくだけなら、
- 前回の比較が
true
だったのかどうか - 前回の比較における右辺
が取れればいけるだろうという感じ。
初めに考えていた方針
Comparable
をいい感じに魔改造すればいけそう- いい感じのオブジェクト は
TrueClass
,FalseClass
あたりを継承すれば良さそう
とぼんやり考えていたところ、
Comparable
を変更してもInteger
の挙動が変わらないTrueClass
,FalseClass
が Singleton なので継承するとnew
できない
あたりの問題が起こったので、Comparable
じゃなく Integer
を直接触るようにしつつ、比較結果オブジェクトをそのまま条件式で使えるようにするのは諦めて to_b
を呼んで boolean に変換するアプローチに変更。
実装
比較結果を保持するオブジェクトComparisonResult::TrueClass
, ComparisonResult::FalseClass
を作って、Integer#<
, Integer#>
がそれを返すように変更。
コードとしてはこんな感じ。
require 'singleton' module ComparisonResult class TrueClass def initialize(left, right) @left = left @right = right end def to_b true end def <(other) @right < other end def >(other) @right > other end end class FalseClass include Singleton def to_b false end def <(_) self end def >(_) self end end end class Integer def <(other) if (self <=> other) == -1 ComparisonResult::TrueClass.new(self, other) else ComparisonResult::FalseClass.instance end end def >(other) if (self <=> other) == 1 ComparisonResult::TrueClass.new(self, other) else ComparisonResult::FalseClass.instance end end end # < raise '1 < 2' unless (1 < 2).to_b == true raise '2 < 1' unless (2 < 1).to_b == false raise '1 < 2 < 3' unless (1 < 2 < 3).to_b == true raise '1 < 3 < 2' unless (1 < 3 < 2).to_b == false raise '2 < 1 < 3' unless (2 < 1 < 3).to_b == false raise '2 < 3 < 1' unless (2 < 3 < 1).to_b == false raise '3 < 1 < 2' unless (3 < 1 < 2).to_b == false raise '3 < 2 < 1' unless (3 < 2 < 1).to_b == false # > raise '1 > 2' unless (1 > 2).to_b == false raise '2 > 1' unless (2 > 1).to_b == true raise '1 > 2 > 3' unless (1 > 2 > 3).to_b == false raise '1 > 3 > 2' unless (1 > 3 > 2).to_b == false raise '2 > 1 > 3' unless (2 > 1 > 3).to_b == false raise '2 > 3 > 1' unless (2 > 3 > 1).to_b == false raise '3 > 1 > 2' unless (3 > 1 > 2).to_b == false raise '3 > 2 > 1' unless (3 > 2 > 1).to_b == true
感想
一応動く何かはできたけど、思っていたことは全然できなかった。
対象クラス・境界値・優先順位あたりはゴリゴリやればいけるはずだけど、 to_b
が生えているのが「これはない……」という感じ。