Scala案例类:避免重新计算属性

时间:2015-03-12 15:56:50

标签: scala

我问这个是因为我多次遇到过这个用例。

假设我们有一个这样的案例类:

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数据提取。

如何避免这种重新计算(请参阅第二个代码段),保持代码优雅?

4 个答案:

答案 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添加到构造函数中,以便xsmax保持一致。

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)
}