Instructor: Stefan Mitsch
How to get recursive iteration without stack penalty?
StackOverflowError
def countDown (x:Int) : Int = if x == 0 then 0 else 1 + countDown (x - 1)
(1 + ...)
countDown (5) --> 1 + countDown (4) --> 1 + (1 + countDown (3)) --> 1 + (1 + (1 + countDown (2))) --> 1 + (1 + (1 + (1 + countDown (1)))) --> 1 + (1 + (1 + (1 + (1 + countDown (0))))) --> 1 + (1 + (1 + (1 + (1 + 0))))
// *tail-recursive functions* because all recursive calls are tail-recursive def countDownAux (x:Int,result:Int) : Int = if x == 0 then result else countDownAux(x-1,1+result) // *tail-recursive call* def countDown (int x) = countDownAux(x,0)
countDownAux (5,0) --> countDownAux (4,1) --> countDownAux (3,2) --> countDownAux (2,3) --> countDownAux (1,4) --> countDownAux (0,5)
f
g
typedef struct node node; struct node { int item; node *next; }; int sum_aux (node *data, int result) { if (!data) { return result; } else { return sum_aux (data->next, result + data->item); } } int sum (node *data) { return sum_aux (data, 0); }
$ gcc -std=c99 -O2 -S tail-recursion2.c
sum_aux: testq %rdi, %rdi movl %esi, %eax je .L7 .L9: addl (%rdi), %eax movq 8(%rdi), %rdi testq %rdi, %rdi jne .L9 .L7: rep ret
sublist
(define (long-list n) (if (= n 0) '(1) (let ([sublist (long-list (- n 1))]) (append sublist sublist))))
$ csi #;1> (long-list 0) (1) #;2> (long-list 1) (1 1) #;3> (long-list 2) (1 1 1 1) #;4> (long-list 4) (1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) #;6> (length (long-list 20)) 1048576
(define (sum-tail-recursive xs) (let loop ([xs xs] [result 0]) (if (eq? xs '()) result (loop (cdr xs) (+ (car xs) result))))) (define (sum xs) (if (eq? xs '()) 0 (+ (car xs) (sum (cdr xs)))))
$ csi #;8> (sum-tail-recursive (long-list 20)) 1048576 #;10> (sum (long-list 20)) Segmentation fault
def longList (n:Int) : List[Int] = if n == 0 then List (1) else val sublist = longList (n - 1) sublist ::: sublist
tailrec
import scala.annotation.tailrec def sumTailRecursive (xs:List[Int]) : Int = @tailrec def aux (xs:List[Int], result:Int) : Int = xs match case Nil => result case y::ys => aux (ys, y + result) aux (xs, 0)
scala> longList (20).length res0: Int = 1048576 scala> sumTailRecursive (longList (20)) res1: Int = 1048576
import scala.annotation.tailrec def sumTailRecursive (xs:List[Int]) : Int = @tailrec def aux (xs:List[Int], result:Int) : Int = xs match case Nil => result case y::ys => 1 + aux (ys, y + result) // bogus "1 + ..." aux (xs, 0)
error: could not optimize @tailrec annotated method aux: it contains a recursive call not in tail position
def fib(n:Int) : Long = if n <= 1 then n else fib(n-1) + fib(n-2)
fib(0)
fib(1)
fib(2)
fib(3)
fib(4)
fib(5)
fib(6)
fib(7)
fib(8)
def fib(n:Int) : (Long, Long) = if n <= 1 then (0, n) else val (a, b) = fib(n-1) (b, a+b)
def fib(n:Int, a:Long=0, b:Long=1) : Long = if n == 0 then a else if n == 1 then b else fib(n-1, b, a+b)
TODO: attempt to translate into a loop
* Time complexity $O(n)$ (additional penalty for activation records) * Space complexity $O(n)$
* Time complexity $O(n)$ (no penalty for creating activation records) * Space complexity $O(1)$