6

所以我知道按需调用只是按名称调用的记忆版本。在 Martin Odersky 在 Coursera 上的 FP 课程中,在第 7.3 课(惰性评估)中,他提到如果 Streams 是使用按名称调用实现的,那么它可能会导致计算复杂性的爆炸。

这种爆炸的例子是什么?

直呼:

def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
  def head = hd
  def tail = tl
  ...
}

按需调用:

def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
  def head = hd
  lazy val tail = tl
  ...
}
4

2 回答 2

2

例如斐波那契数列,通过添加前两个元素形成后继元素来实现。如果没有记忆,序列长度会线性减慢(和堆栈增长):

scala> lazy val fib: Stream[Int] = Stream.cons(0,
     | Stream.cons(1, fib.zip(fib.tail).map(p => p._1 + p._2)))
fib: Stream[Int] = Stream(0, ?)

懒惰的例子(-sic-)从这个博客复制

于 2013-05-07T18:43:12.753 回答
0

def并且lazy val都很懒惰。不同之处在于def每次调用时都会重新评估。lazy val仅在第一次调用时才被评估,对它的任何调用都只会查看第一个评估值。

使用def意味着tail每次调用时都会重新评估。

def randDef: () => Int = {
  val r = scala.util.Random.nextInt
  () => r
}

val randVal: () => Int = {
  val r = scala.util.Random.nextInt
  () => r
}

lazy val randLazyVal: () => Int = {
  val r = scala.util.Random.nextInt
  () => r
}
// defined function randDef
// randVal: () => Int = ammonite.$sess.cmd18$Helper$$Lambda$2545/0x00000008013a4430@7466fa3
// randLazyVal: () => Int = [lazy]

randDef()
// res8: Int = -693604502
randDef()
// res9: Int = 2056941756
randDef eq randDef
// res15: Boolean = false

randVal()
// res11: Int = 3
randVal()
// res12: Int = 3
randVal eq randVal
// res16: Boolean = true

randLazyVal()
// res13: Int = 99
randLazyVal()
// res14: Int = 99
randLazyVal eq randLazyVal
// res17: Boolean = true

于 2022-01-13T01:11:27.150 回答