Любая рекурсивная функция может быть преобразована для использования кучи, а не стека, для отслеживания контекста. Процесс называется trampolining
.
Вот как это можно реализовать с помощью Scalaz.
object TrampolineUsage extends App {
import scalaz._, Scalaz._, Free._
def quickSort[T: Order](xs: List[T]): Trampoline[List[T]] = {
assert(Thread.currentThread().getStackTrace.count(_.getMethodName == "quickSort") == 1)
xs match {
case Nil =>
return_ {
Nil
}
case x :: tail =>
val (left, right) = tail.partition(_ < x)
suspend {
for {
ls <- quickSort(left)
rs <- quickSort(right)
} yield ls ::: (x :: rs)
}
}
}
val xs = List.fill(32)(util.Random.nextInt())
val sorted = quickSort(xs).run
println(sorted)
val (steps, sorted1) = quickSort(xs).foldRun(0)((i, f) => (i + 1, f()))
println("sort took %d steps".format(steps))
}
Конечно, вам нужна либо действительно большая структура, либо действительно маленький стек, чтобы иметь практическую проблему с нерекурсивно-рекурсивным алгоритмом деления и завоевания, так как вы можете обрабатывать 2 ^ N элементов с глубиной стека N.
http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html
UPDATE
scalaz.Trampoline
является частным случаем (гораздо) более общей структуры, Free
. Это определяется как type Trampoline[+A] = Free[Function0, A]
. На самом деле можно написать quickSort
более обобщенно, поэтому он параметризуется конструктором типов, используемым в Free
. В этом примере показано, как это сделать, и как вы можете использовать тот же код для привязки, используя стек, кучу или одновременно.
https://github.com/scalaz/scalaz/blob/scalaz-seven/example/src/main/scala/scalaz/example/TrampolineUsage.scala