有可能有一个懒惰地评估其元素的数组吗?

时间:2011-07-01 17:39:02

标签: arrays performance caching scala lazy-evaluation

考虑这个BigInt类,它应该缓存smallValues中的一些常见值:

object BigInt {
  lazy val smallValues = Array(Zero, One, Two)
  lazy val Zero = new BigInt(0, Array[Long]())
  lazy val One = new BigInt(1, Array[Long](1))
  lazy val Two = new BigInt(1, Array[Long](2)) 

  private lazy val cacheSize = smallValues.length


  def apply(num: Long): BigInt = {
    // Is the number cached?
    if (0 <= num && num < cacheSize) smallValues(num.toInt)
    // Figure out the sign and make the number positive after that
    else {
      val (sign, value) = if (num < 0) (-1, num * -1) else (1, num)
      new BigInt(sign, Array(value))
    }
  }
}

class BigInt private(val sign: Int, val num: Array[Long]) extends Ordered[BigInt] {
  println("Constructing BigInt")
  ...
}

这里的问题是访问数组的一个元素会强制评估所有元素:

scala> BigInt.smallValues(0)
Constructing BigInt
Constructing BigInt
Constructing BigInt
res0: BigInt = BigInt@2c176570

我怎么能解决这个问题?

编辑:看看提出的解决方案,我真的很想知道如果没有进一步的复杂化就分配它们会不会更有效率。你觉得怎么样?

2 个答案:

答案 0 :(得分:4)

编辑我的答案,因为我认为这是你想要做的事情的玩具示例,而你的真实物品是如此昂贵的构建,懒惰给你带来了什么。如果问题显示更像真实代码的东西,那么懒惰毫无意义。懒惰的对象比严格的对象更大,更昂贵。尽管如此,我仍然保留以下代码,因为它确实显示了如何创建一个懒惰的包装器并且它确实“工作”(从某种意义上说它在功能上是正确的)即使它在一个好的意义上不“工作”对你的用例的想法。

class Lazy[T] (expr : => T) {lazy val ! = expr}
object Lazy{def apply[T](expr : => T) = new Lazy({expr})}

class BigInt (val sign: Int, val num: Array[Long]) {
  println("Constructing BigInt")
}

object BigInt {
  val smallValues = Array(
    Lazy(new BigInt(0, Array[Long]())),
    Lazy(new BigInt(1, Array[Long](1))),
    Lazy(new BigInt(1, Array[Long](2)))
  )

  private val cacheSize = smallValues.length.toLong

   def apply(num: Long): BigInt = {
    // Is the number cached?
    if (0 <= num && num < cacheSize) smallValues(num.toInt)!
    // Figure out the sign and make the number positive after that
    else {
      val (sign, value) = if (num < 0) (-1, num * -1) else (1, num)
      new BigInt(sign, Array(value))
    }
  }
}


scala> BigInt(1)
Constructing BigInt
res0: BigInt = BigInt@c0dd841

scala> BigInt(1)
res1: BigInt = BigInt@c0dd841

scala> BigInt(2)
Constructing BigInt
res2: BigInt = BigInt@4a6a00ca

scala> BigInt(2)
res3: BigInt = BigInt@4a6a00ca

答案 1 :(得分:3)

创建你自己的懒惰包装器,可能带有隐式转换,所以你没有注意到你正在使用它:

class Lazy[A](a0: => A) { lazy val value = a0 }
implicit def lazy_to_actual[a](lz: Lazy[A]) = lz.value

然后:

lazy val smallValues = Array(
  new Lazy(new BigInt(0, new Array[Long]())),
  new Lazy(new BigInt(1, new Array[Long](1))),
  new Lazy(new BigInt(2, new Array[Long](2)))
)

虽然如果我是你,我可能会改用地图。