我问这个是因为我多次遇到过这个用例。
假设我们有一个这样的案例类:
case class C(xs: Iterable[Int]) {
val max = xs.max
def ++(that: C) = C(xs ++ that.xs)
}
这样可以正常工作,但++
操作效率很低,因为集合会不必要地再次遍历以计算结果的最大值;既然我们已经知道了两个集合的最大值,我们可以重用它 - 通过使用类似的东西:
def ++(that: C) =
C(xs ++ that.xs, max = math.max(max, that.max))
这只是一个演示目的的简单示例 - 避免的计算可能要复杂得多,甚至可能是TCP数据提取。
如何避免这种重新计算(请参阅第二个代码段),保持代码优雅?
答案 0 :(得分:3)
这样的事情会起作用
class C private (val xs: Iterable[Int], val max: Int) {
def ++(that: C) = new C(xs ++ that.xs, math.max(this.max, that.max)
}
object C {
def apply(xs: Iterable[Int]) = new C(xs, xs.max)
}
请注意,C不再是一个案例类,以避免max和xs变得不一致。如果C是一个案例类,你可以调用例如c.copy(max = -1)并得到一个不一致的实例。
答案 1 :(得分:2)
case class C(xs: Iterable[Int]) {
private var maxOp = Option.empty[Int]
lazy val max = maxOp getOrElse {
maxOp = Some(xs.max)
maxOp.get
}
def ++(that: C) = {
val res = C(xs ++ that.xs)
res.maxOp = Some(math.max(this.max, that.max))
res
}
}
答案 2 :(得分:1)
由于max
已经是val
(与方法不同),您可以这样做:
case class C private (xs: Iterable[Int], max: Int) {
def ++(that: C) = C(xs ++ that.xs, math.max(max, that.max))
def copy(_xs: Iterable[Int] = this.xs) = {
if (_xs == this.xs) {
C(xs, max)
} else {
C(_xs)
}
}
}
object C {
def apply(xs: Iterable[Int]): C = C(xs, xs.max)
}
如果您打算在此案例类中进行模式匹配,那么它取决于您的用例,如果您也可以(或必须)在max
上进行模式匹配。
更新1 正如Rüdiger所指出的,我已将private
添加到构造函数中,以便xs
和max
保持一致。
Update 2 正如som-snytt所指出的那样,必须处理copy
方法以防止出现不一致。
答案 3 :(得分:0)
sealed trait C {
val xs: Iterable[Int]
val max: Int
def ++(that: C) = ComposedC(this, that)
}
case class ValidatedC(xs: Iterable[Int]) extends C {
val max = xs.max
}
case class ComposedC(a: C, b: C) extends C {
val max = math.max(a.max, b.max)
val xs = a.xs ++ b.xs
}
object C {
def apply(xs: Iterable[Int]) = ValidatedC(xs)
}
更简单的解决方案(不强制执行正确性) -
介绍一种提供预先计算的max
和一个获得2 Cs的辅助构造函数的方法。
case class C(xs: Iterable[Int])(val max: Int = xs.max) {
def this(a: C, b: C) = {
this(a.xs ++ b.xs)(math.max(a.max, b.max))
}
def ++(that: C) = new C(this, that)
}