11

在以下情况下

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)。

4

2 回答 2

12

为特征生成尾递归方法。特征扩展器中的方法入口(此处O)将调用转发到特征的方法,但在这样做的同时,它保留对流头部的引用。

因此该方法是尾递归的,但内存仍然无法释放。补救措施:不要在特征中定义流函数,直接在对象中。

另一种方法是 scalaz's EphemeralStream,它持有对流头部和尾部的弱引用,并根据需要重新计算它们。

于 2012-09-18T23:57:41.800 回答
3

有一个简单的解决方法。只需将您的尾递归流消费者包装在另一个通过名称参数接收流的函数中:

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) 
}

forwarder 方法将持有对计算表达式的函数的引用,该表达式的结果是一个流:

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();
      }
    });
  }
}
于 2014-07-02T03:14:46.880 回答