プログラミング言語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 メソッドにまとめてそれを呼べれば良かったんだが…。
また、比較自体も分母が異なっていれば相手側の分子にかける、という
結構大ざっぱな計算をやっているような印象がある。
もうちょっとスマートなやり方はないものか…。