In Programming in Scala there is an example Rational class which
begins
class Rational(n: Int, d: Int) {
require(d != 0)
private val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g
…
I just want to verify that g sticks around for the life of the
object so
that 3 values are stored instead of just 2. If space was more
important
than time, this would be more efficient:
That’s correct, g
would occupy the space of one Int
.
You could also write
class Rational(n: Int, d: Int) {
require(d != 0)
val (numer, denom) = {
val g = gcd(n.abs, d.abs)
(n/g, g/d)
}
}
although it might perform slightly worse due to the temporary
construction of a tuple. Or use a companion object
object Rational {
def apply(n: Int, d: Int): Rational = {
require(d != 0)
val g = gcd(n.abs, d.abs)
new Rational(n/g, g/d)
}
}
class Rational private(val numer: Int, val denom: Int)
Probably this would be the best form.
It seems like a templated Rational[T] would be effective,
but not just any T has an abs function (among others). I can’t figure
out how to use Integral or anything like it in the template. Can someone
show a way to have
There are so-called type classes for numeric operations: Numeric
(addition, multiplication), Fractional (adds division), Integral (adds
modulus). You need Integral because gcd requires modulus.
There are default instances for Int, Long and others
implicitly[Integral[Long]] // -> scala.math.Numeric.LongIsIntegral
(Numeric)
These have the necessary math operations, e.g.
scala.math.Numeric.LongIsIntegral.rem(10L, 6L) // 4
If you import the members of such an instance of Integral, you can use
‘operators’ almost as before (because it provides extension methods
through mkNumericOps
). So instead of
def gcd[A](a: A, b: A)(implicit num: Integral[A]): A = {
if (b == num.zero) a else gcd(b, num.rem(a, b))
}
you can write
def gcd[A](a: A, b: A)(implicit num: Integral[A]): A = {
import num._
if (b == zero) a else gcd(b, a % b)
}
Then the whole class could be written
object Rational {
def apply[A](n: A, d: A)(implicit num: Integral[A]): Rational[A] = {
import num._
require (d != zero)
val g = gcd(n.abs, d.abs)
new Rational(n / g, d / g)
}
private def gcd[A](a: A, b: A)(implicit num: Integral[A]): A = {
import num._
if (b == zero) a else gcd(b, a % b)
}
}
class Rational[A] private (val numer: A, val denom: A)(implicit num:
Integral[A]) {
import num._
override def toString = s"$numer/$denom"
def + (that: Rational[A]): Rational[A] =
Rational(numer * that.denom + that.numer * denom,
denom * that.denom)
}
Test:
Rational(4L, 8L) + Rational(5L, 9L) // 19/18
best, .h.h.