LoginSignup
1
1

More than 5 years have passed since last update.

自作の有理数クラスに比較演算子をつけたい

Posted at

プログラミング言語Scala 日本語情報サイトなどを参考に自作した有理数同士を比較するため、
このクラスに比較演算子をつけてみた。
が、これでいいのだろうか…。

Rational.scala
case class Rational(n: Int, d: Int) {
  private def gcd(x: Int, y: Int): Int = {
    if (x == 0) y
    else if (x < 0) gcd(-x, y)
    else if (y < 0) -gcd(x, -y)
    else gcd(y % x, x)
  }
  private val g: Int = gcd(n, d)

  val numer: Int = n / g
  val denom: Int = d / g
  def +(that: Rational): Rational = Rational(numer * that.denom + that.numer * denom, denom * that.denom)
  def -(that: Rational): Rational = Rational(numer * that.denom - that.numer * denom, denom * that.denom)
  def *(that: Rational): Rational = Rational(numer * that.numer, denom * that.denom)
  def /(that: Rational): Rational = Rational(numer * that.denom, denom * that.numer)

  override def toString(): String = {
    s"${n}/${d}"
  }
  override def equals(other: Any): Boolean = {
    if (other.isInstanceOf[Rational]) {
      this == other.asInstanceOf[Rational]
    } else {
      false
    }
  }

  def <(that: Rational): Boolean = {
    if (this.denom == that.denom) {
      this.numer < that.numer
    } else {
      ((this.numer * that.denom) < (that.numer * this.denom))
    }
  }

  def <=(that: Rational): Boolean = {
    if (this.denom == that.denom) {
      this.numer <= that.numer
    } else {
      ((this.numer * that.denom) <= (that.numer * this.denom))
    }
  }

  def >(that: Rational): Boolean = {
    if (this.denom == that.denom) {
      this.numer > that.numer
    } else {
      ((this.numer * that.denom) > (that.numer * this.denom))
    }
  }
  def >=(that: Rational): Boolean = {
    if (this.denom == that.denom) {
      this.numer >= that.numer
    } else {
      ((this.numer * that.denom) >= (that.numer * this.denom))
    }
  }

  def ==(that: Rational): Boolean = {
    if (this.denom == that.denom) {
      this.numer == that.numer
    } else {
      ((this.numer * that.denom) == (that.numer * this.denom))
    }
  }
  def !=(that: Rational): Boolean = ! ==(that)
}

前半部分は上記サイトからほぼコピペ。
case class にしたのは、equals を == 演算子と同義にしたかったためオーバーライドし、
合わせて hashCode もオーバーライドするのが面倒だったので。
(case class にするかどうかの判断もこれでいいのか悩ましい。)

後半部分が比較演算子。
分母が同じであれば分子同士を比較し、分母が異なれば分子に比較相手側の分母をかけて比較する。

とりあえずやりたいことはできているが、
それぞれのメソッドで演算子以外ほぼ同じことをやっているのでもう少し共通化したかったが、
うまいやり方が分からなかった。
イメージとしては、

  // こんなことはできない
  def <(that: Rational): Boolean = operate(<, that)
  def <=(that: Rational): Boolean = operate(<=, that)
  def >(that: Rational): Boolean = operate(>, that)
  def >=(that: Rational): Boolean = operate(>=, that)
  def ==(that: Rational): Boolean = operate(==, that)
  def !=(that: Rational): Boolean = operate(!=, that)

  private def operate(operator:(Int => Boolean), that:Rational): Boolean = {
    if (this.denom == that.denom) {
      this.numer operator that.numer
    } else {
      ((this.numer * that.denom) operator (that.numer * this.denom))
    }
  }

こんな感じで operate メソッドにまとめてそれを呼べれば良かったんだが…。

また、比較自体も分母が異なっていれば相手側の分子にかける、という
結構大ざっぱな計算をやっているような印象がある。

もうちょっとスマートなやり方はないものか…。

1
1
4

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
1
1