The following blog article shows how in F# foldBack
can be made tail recursive using continuation passing style.
In Scala this would mean that:
I'm late to this question, but I wanted to show how you can write a tail-recursive FoldRight without using a full trampoline; by accumulating a list of continuations (instead of having them call each other when done, which leads to a stack overflow) and folding over them at the end, kind of like keeping a stack, but on the heap:
object FoldRight {
def apply[A, B](list: Seq[A])(init: B)(f: (A, B) => B): B = {
@scala.annotation.tailrec
def step(current: Seq[A], conts: List[B => B]): B = current match {
case Seq(last) => conts.foldLeft(f(last, init)) { (acc, next) => next(acc) }
case Seq(x, xs @ _*) => step(xs, { acc: B => f(x, acc) } +: conts)
case Nil => init
}
step(list, Nil)
}
}
The fold that happens at the end is itself tail-recursive. Try it out in ScalaFiddle
In terms of performance, it performs slightly worse than the tail call version.
[info] Benchmark (length) Mode Cnt Score Error Units
[info] FoldRight.conts 100 avgt 30 0.003 ± 0.001 ms/op
[info] FoldRight.conts 10000 avgt 30 0.197 ± 0.004 ms/op
[info] FoldRight.conts 1000000 avgt 30 77.292 ± 9.327 ms/op
[info] FoldRight.standard 100 avgt 30 0.002 ± 0.001 ms/op
[info] FoldRight.standard 10000 avgt 30 0.154 ± 0.036 ms/op
[info] FoldRight.standard 1000000 avgt 30 18.796 ± 0.551 ms/op
[info] FoldRight.tailCalls 100 avgt 30 0.002 ± 0.001 ms/op
[info] FoldRight.tailCalls 10000 avgt 30 0.176 ± 0.004 ms/op
[info] FoldRight.tailCalls 1000000 avgt 30 33.525 ± 1.041 ms/op