Fla*_*gan 9 memory-leaks scala
我正在阅读"Scala编程"一书,并Rational在第6章的实现中遇到了一些问题.
这是我Rational班级的初始版本(基于本书)
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
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是,字段g在类的生命周期内保持不变,即使从未再次访问过.运行以下模拟程序可以看到此问题:
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")
}
}
Run Code Online (Sandbox Code Playgroud)
它的输出将是:
Field: denom
2
Field: numer
1
Field: g
1
Run Code Online (Sandbox Code Playgroud)
我在Scala Wiki上找到的解决方案涉及以下内容:
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
}
Run Code Online (Sandbox Code Playgroud)
这里,字段g只对其块是本地的,但是,运行小测试应用程序,我发现另一个字段x$1保留了由元组组成的元组的副本(numer, denom)!
Field: denom
2
Field: numer
1
Field: x$1
(1,2)
Run Code Online (Sandbox Code Playgroud)
有没有办法用上面的算法在Scala中构造一个理性,而不会导致任何内存泄漏?
谢谢,
Flaviu Cipcigan
Tho*_*ung 13
配套对象可以提供您所需的灵活性.它可以定义替换构造函数的"静态"工厂方法.
object Rational{
def apply(numerator: Int, denominator: Int) = {
def gcd(a: Int, b: Int): Int = if(b == 0) a else gcd(b, a % b)
val g = gcd(numerator, denominator)
new Rational(numerator / g, denominator / g)
}
}
class Rational(numerator: Int, denominator: Int) {
require(denominator != 0)
override def toString = numerator + "/" + denominator
// other methods go here, neither access g
}
val r = Rational(10,200)
Run Code Online (Sandbox Code Playgroud)
在工厂方法的范围内,g可以计算并用于导出两个构造函数值.
小智 13
你可以这样做:
object Rational {
def gcd(a: Int, b: Int): Int =
if(b == 0) a else gcd(b, a % b)
}
class Rational private (n: Int, d: Int, g: Int) {
require(d != 0)
def this(n: Int, d: Int) = this(n, d, Rational.gcd(n.abs, d.abs))
val numer = n / g
val denom = d / g
override def toString = numer + "/" + denom
}
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
val numer = numerator / gcd(numerator.abs, denominator.abs)
val denom = denominator / gcd(numerator.abs, denominator.abs)
Run Code Online (Sandbox Code Playgroud)
当然你必须做两次计算.但是,优化通常是内存/空间和执行时间之间的权衡.
也许还有其他方法,但随后程序可能变得过于复杂,如果有一个地方优化很少为时过早,那就是脑力优化:).例如,您可能会这样做:
val numer = numerator / gcd(numerator.abs, denominator.abs)
val denom = denominator / (numerator / numer)
Run Code Online (Sandbox Code Playgroud)
但它并不一定使代码更容易理解.
(注意:我实际上没有尝试过,所以请自担风险.)
托马斯·荣格的例子有一个小问题; 它仍允许您创建一个在分子和分母中具有公共术语的Rational对象 - 如果您自己使用'new'而不是通过伴随对象创建Rational对象:
val r = new Rational(10, 200) // Oops! Creating a Rational with a common term
Run Code Online (Sandbox Code Playgroud)
您可以通过要求客户端代码始终使用伴随对象来创建Rational对象,通过使隐式构造函数为private来避免这种情况:
class Rational private (numerator: Int, denominator: Int) {
// ...
}
Run Code Online (Sandbox Code Playgroud)