在以下情况下
trait T {
@tailrec
def consume[A](as: Stream[A]): Unit = {
if (as.isEmpty) ()
else consume(as.tail)
}
}
object O extends T
调用O.consume(Range(1, N).toStream)
N
足够大,程序将耗尽内存,或者至少会消耗 O(N) 而不是所需的 O(1)。
为特征生成尾递归方法。特征扩展器中的方法条目(此处O
)将调用转发给特征的方法,但在这样做时,它会保留对流头的引用。
因此,该方法是尾递归的,但仍然无法释放内存。补救措施:不要在特征中定义 Stream 函数,而直接在对象中定义。
另一种选择是 scalaz 的 EphemeralStream
,它保存对流头和尾的弱引用,并根据需要重新计算它们。
有一个简单的解决方法。只需将您的尾递归流使用者包装在另一个函数中,该函数通过 by-name 参数接收流:
import scala.annotation.tailrec
trait T {
def consume[A](as: => Stream[A]): Unit = {
@tailrec
def loop[A](as: Stream[A]): Unit = {
if (as.isEmpty) ()
else loop(as.tail)
}
loop(as)
}
}
object O extends T {
def main(args: Array[String]): Unit =
O.consume(Range(1, 1000000000).toStream)
}
转发器方法将保存对函数的引用,该函数计算表达式的结果是流:
public final class O$ implements T {
public static final MODULE$;
// This is the forwarder:
public <A> void consume(Function0<Stream<A>> as) {
T.class.consume(this, as);
}
. . .
public void main(String[] args) {
consume(new AbstractFunction0() {
public final Stream<Object> apply() {
return package..MODULE$.Range().apply(1, 1000000000).toStream();
}
});
}
}