お題:時間帯重複チェックを解いてみた。

はじめに

具体的なお題を解いていくと言語を効率よく勉強できるので、「お題:時間帯重複チェック - No Programming, No Life」に乗っからせていただきました。

最初のコード

Groovyは勉強中で発想が貧困なので、まずはベタなコードを書くことから始めました。

さすがにベタすぎてGroovyらしさがないだけでなく、isNotGreaterThanとかセンスの欠片もありませんね。(^^;;

次のコード

次にhourとminutesをメンバにもつクラスMyTimeを作ってみることにしました。
独自のクラスではなく、Dateを使おうかとも思ったのですが、

  • 年月日秒の概念が余計である
  • お題で(24,0)がアリになっている
  • 使わない方が勉強になるかも

という理由でMyTimeを作ることにしました。


クラス化したことで「宇宙船演算子 <=>」(Groovyインアクション P55参照)が使えることに気づきました。
そこで、宇宙船演算子を使うために、MyTimeに

  • Comparableインタフェースを実装して
  • compareToメソッドを定義しました

これで、MyTime同士の比較が次のように素直に表現できるようになりました。(例えば、r1.start <= r1.endの部分)

ここでは、

  • @Newifyを使って new を省いたり
  • 生成直後に有効なインスタンスにしたいので isValid をコンストラクタに入れたり
  • エラーメッセージの可読性をあげるために toString を定義したり
  • 入力値と期待値を Map で定義して可読性を向上させたり

といったちょっとした配慮も加えています。

最後のコード

クラス化したことで少しマシなコードになりました。しかし、開始時刻と終了時刻の関係性が薄い気がしてどうもすっきりしません。
お題の (1, 0, 5, 30) という書き方は 「開始時刻と終了時刻で一つのグループを構成する」 ということを表現できているので、意味的にこの表現に近づけたいと思っていました。

そんなこんなでモヤモヤと考えていたところ、Rangeにすれば良いことに気づきました。Range なら「開始」と「終了」を明確に表現できますし、お題のゴールである「重なり」も intersect でズバリ表現できます。
そこで、Range化してintersectが使えるようにするために、MyTimeに

  • nextメソッドと
  • previousメソッドを

定義して次のコードをこしらえました。

お題をクリアするためには、範囲1と範囲2に対して、

  • 範囲1が範囲2を完全に包含している
  • 範囲2が範囲1を完全に包含している
  • 範囲1が左、範囲2が右に位置して中間に重なりがある
  • 範囲2が左、範囲1が右に位置して中間に重なりがある

の4つを表現しなければなりませんが、intersectを使えるようにしたことで、Groovyがよしなに処理してくれるようになりました。(^^

おわりに

今回のお題も色々と発見があり、Groovy力が少し上がりました。お題を提供してくださった id: fumokmm さんありがとうございました。