迭代过程的流与尾递归

时间:2011-12-22 10:46:35

标签: scala stream tail-recursion

这是my previous question的后续行动。

我知道我们可以使用streams来生成'pi'(和其他数字),第n个斐波那契等的近似值。但我怀疑是否{{1}这是正确的方法。

主要缺点(我认为)是内存消耗:例如streams将保留i <所有的斐波纳契数。我虽然只需要斐波纳契。当然,我可以使用stream,但它使解决方案更复杂一些。 drop看起来像是一种更适合这类任务的方法。

您怎么看?

3 个答案:

答案 0 :(得分:1)

如果需要快速行驶,请轻装上阵。那意味着;避免分配任何不必要的内存。如果您需要内存,请使用可用的快速收集。如果你知道你需要多少记忆;预分配。分配是绝对的性能杀手......用于计算。你的代码可能看起来不太好了,但它会很快。

但是,如果您正在使用IO(磁盘,网络)或任何用户交互,那么分配就会失败。最好将优先级从代码性能转移到可维护性。

答案 1 :(得分:1)

使用Iterator。它不保留中间值。

答案 2 :(得分:1)

如果你想要第n个斐波纳契数并使用流作为临时数据结构(如果你不保留对先前计算的流元素的引用),那么你的算法将在恒定空间中运行。 之前计算的Stream元素(不再使用)将被垃圾收集。由于它们是在最年轻的一代中分配并立即收集的,因此所有分配都可能在缓存中。

更新:

似乎Stream的当前实现并不像它可能的那样节省空间,主要是因为它从LinearSeqOptimized trait继承了apply方法的实现,其中它被定义为


def apply(n: Int): A = {
   val rest = drop(n)
   if (n < 0 || rest.isEmpty) throw new IndexOutOfBoundsException("" + n)
   rest.head
}
此处由this保留对流的头部的引用,并防止流被gc'ed。因此,drophead方法的组合(如f.drop(100).head)可能更适合丢弃中间结果的情况。 (感谢Sebastien Bocq在scala-user上解释这些内容)。