I was working through the "Programming in Scala" book, and was struck by a bit of a problem in the implementation of the class Rational
in Chapter 6.
This is my initial version of the Rational
class (based on the book)
class Rational(numerator: Int, denominator: Int) {
require(denominator != 0)
private val g = gcd(numerator.abs, denominator.abs)
val numer = numerator / g
val denom = denominator / g
override def toString = numer + "/" + denom
private def gcd(a: Int, b: Int): Int =
if(b == 0) a else gcd(b, a % b)
// other methods go here, neither access g
}
The problem here is that the field g remains for the lifetime of the class, even if never again accessed. This problem can be seen by running the following mock program:
object Test extends Application {
val a = new Rational(1, 2)
val fields = a.getClass.getDeclaredFields
for(field <- fields) {
println("Field name: " + field.getName)
field.setAccessible(true)
println(field.get(a) + "\n")
}
}
Its output is going to be:
Field: denom
2
Field: numer
1
Field: g
1
A solution I found at the Scala Wiki involves the following:
class Rational(numerator: Int, denominator: Int) {
require(denominator != 0)
val (numer, denom) = {
val g = gcd(numerator.abs, denominator.abs)
(numerator / g, denominator / g)
}
override def toString = numer + "/" + denom
private def gcd(a: Int, b: Int): Int =
if(b == 0) a else gcd(b, a % b)
// other methods go here
}
Here, the field g is only local to its block, but, running the small test application, I found another field x$1
which keeps a copy of the tuple consisting of (numer, denom)
!
Field: denom
2
Field: numer
1
Field: x$1
(1,2)
Is there any way to construct a rational in Scala with the above algorithm, without causing any memory leaks?
Thanks,
Flaviu Cipcigan
You could do this: