Хвостовая рекурсивная функция процессора Scala, определенная в trait, содержит ссылку на заголовок потока

В следующей ситуации

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

2 ответа

Решение

Хвосто-рекурсивный метод генерируется для признака. Метод ввода в расширитель признака (здесь O) перенаправляет вызов методу признака, но при этом сохраняет ссылку на начало потока.

Таким образом, метод хвостовой рекурсии, но память все еще не может быть освобождена. Помощь: Не определять функции Stream в чертах, просто непосредственно в объектах.

Альтернативой является скаляр EphemeralStream, который содержит слабые ссылки на начало и конец потока и пересчитывает их по требованию.

Есть простой обходной путь. Просто оберните своего хвостового рекурсивного потребителя в другую функцию, которая получает поток через параметр по имени:

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();
      }
    });
  }
}
Другие вопросы по тегам