Al escribir una función que funciona en Stream
(s), existen diferentes conceptos de recursión. El primer sentido simple no es recursivo en el nivel del compilador, ya que la cola si no se evalúa al instante por lo que la función devuelve de inmediato, pero el flujo devuelto es recursivo:¿Cómo escribir la función recursiva de cola sin fugas usando Stream.cons en Scala?
final def simpleRec[A](as: Stream[A]): Stream[B] =
if (a.isEmpty) Stream.empty
else someB(a.head) #:: simpleRec(a.tail)
la idea anterior de la recursividad no causa ningún problema . El segundo es verdaderamente recursiva de cola en el nivel del compilador:
@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
El problema aquí es que el caso C)
es detectado por el compilador como una llamada no tailrec, incluso si no hay una llamada real llevado a cabo. Esto se puede evitar mediante la factorización la cola de corriente en una función auxiliar:
@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)
Mientras que compila, este enfoque eventualmente resulta en una pérdida de memoria. Dado que finalmente se llama al rec
recursivo de cola desde la función recHelp
, el marco de pila de la función recHelp
contiene una referencia al cabezal de vapor y no permite que el flujo sea recogido hasta que devuelve la llamada rec
, que puede ser bastante larga. (en términos de pasos de recursión) según el número de llamadas al B)
.
Tenga en cuenta que incluso en el caso helperless, si el compilador permitía el @tailrec, la pérdida de memoria puede estar presente ya que la cola de flujo diferida crearía en efecto un objeto anónimo con referencia al encabezado de flujo.
Consulte también la http://stackoverflow.com/questions/12486762/scala-tail-recursive-stream-processor-function-defined-in-trait-holds-reference – ron
¿hay alguna posibilidad de que una pieza de código funcione? Es decir. uno que OOMs? – Chris
Chris: seguro, compare los dos: https://gist.github.com/3769565 (2.10.0-M7 no suena para ok.scala) – ron