似乎Iterator和Stream都是懒惰的,并且允许您将元素保留在心脏的内容中.这两者有什么区别?
当编写在Stream(s)上运行的函数时,存在不同的递归概念.第一个简单的意义在编译器级别上不是递归的,因为如果不立即计算尾部,那么函数会立即返回,但返回的流是递归的:
final def simpleRec[A](as: Stream[A]): Stream[B] =
if (a.isEmpty) Stream.empty
else someB(a.head) #:: simpleRec(a.tail)
Run Code Online (Sandbox Code Playgroud)
上述递归概念不会引起任何问题.第二个是在编译器级别上真正的尾递归:
@tailrec
final def rec[A](as: Stream[A]): Stream[B] =
if (a.isEmpty) Stream.empty // A) degenerated
else if (someCond) rec(a.tail) // B) tail recursion
else someB(a.head) #:: rec(a.tail) // C) degenerated
Run Code Online (Sandbox Code Playgroud)
这里的问题是C)编译器将该情况检测为非tailrec调用,即使没有执行实际调用.这可以通过将流尾部分解为辅助函数来避免:
@tailrec
final def rec[A](as: Stream[A]): Stream[B] =
if (a.isEmpty) Stream.empty
else if (someCond) rec(a.tail) // B)
else someB(a.head) #:: recHelp(a.tail)
@tailrec
final def recHelp[A](as: Stream[A]): Stream[B] =
rec(as)
Run Code Online (Sandbox Code Playgroud)
在编译时,这种方法最终会导致内存泄漏.由于尾递归rec最终是从recHelp …
在以下情况
trait T {
@tailrec
def consume[A](as: Stream[A]): Unit = {
if (as.isEmpty) ()
else consume(as.tail)
}
}
object O extends T
Run Code Online (Sandbox Code Playgroud)
主叫O.consume(Range(1, N).toStream)用N足够大的,程序将运行的存储器,或者至少将消耗O(N),而不是所需要的O(1).