Instructor: James Riely
int count_down (int x) {
if (x == 0) {
return 0;
} else {
return 1 + count_down (x - 1);
}
}
int main (int argc, char **argv) {
long num = strtol (argv[1], NULL, 10);
count_down (num);
return 0;
}
$ ./recursion 272001
$ ./recursion 272002
Segmentation fault
$ ./recursion 272001
$ ./recursion 272001
Segmentation fault
$ for x in `seq 261700 261960`; do echo $x; ./recursion $x; done
ulimit
$ ulimit -a
...
stack size (kbytes, -s) 8192
...
$ ulimit -S -s
8192
$ ulimit -H -s
unlimited
$ ulimit -s 65536
$ ./recursion 2000000
$ ./recursion 3000000
Segmentation fault
static int countDown (int x) {
if (x == 0) {
return 0;
} else {
return 1 + countDown (x - 1);
}
}
$ java Recursion 18053
$ java Recursion 18054
Exception in thread "main" java.lang.StackOverflowError
at Recursion.countDown(Recursion.java:6)
at Recursion.countDown(Recursion.java:6)
at Recursion.countDown(Recursion.java:6)
at Recursion.countDown(Recursion.java:6)
...
def countDown (x:Int) : Int = if x == 0 then 0 else 1 + countDown (x - 1)
scala> countDown (59085)
res40: Int = 59085
scala> countDown (59086)
java.lang.StackOverflowError
at .countDown(<console>:7)
at .countDown(<console>:7)
at .countDown(<console>:7)
at .countDown(<console>:7)
...
def countDown (x:Int) : Int = if x == 0 then 0 else 1 + countDown (x - 1)
(1 + ...)
represents a new AR
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))))
int count_down (int x) {
if (x == 0) {
return 0;
} else {
return 1 + count_down (x - 1);
}
}
$ gcc -std=c99 -S recursion.c
count_down:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
cmpl $0, -4(%rbp)
jne .L2
movl $0, %eax
jmp .L3
.L2:
movl -4(%rbp), %eax
subl $1, %eax
movl %eax, %edi
call count_down
addl $1, %eax ; work *after* recursive call
.L3:
leave
ret
int count_down (int x) {
...
return 1 + count_down (x - 1); // work *after* recursive call
...
}
count_down:
pushq %rbp
movq %rsp, %rbp
...
call count_down
addl $1, %eax ; work *after* recursive call
...
leave
ret
// *tail-recursive functions* because all
// recursive calls are tail-recursive
int count_down_aux (int x, int result) {
if (x == 0) {
return result;
} else {
return count_down_aux (x-1, 1+result); // *tail-recursive call*
}
}
int count_down (int x) {
return count_down_aux (x, 0);
}
count_down_aux:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $0, -4(%rbp)
jne .L2
movl -8(%rbp), %eax
jmp .L3
.L2:
movl -8(%rbp), %eax
leal 1(%rax), %edx
movl -4(%rbp), %eax
subl $1, %eax
movl %edx, %esi
movl %eax, %edi
call count_down_aux ; nothing afterwards
.L3:
leave
ret
$ gcc -std=c99 -O2 -S recursion.c
count_down:
movl %edi, %eax
ret
$ gcc -std=c99 -O2 -S tail-recursion.c
count_down_aux:
movl %esi, %eax
testl %edi, %edi
leal (%rdi,%rax), %edx
cmovne %edx, %eax
ret
count_down:
movl %edi, %eax
ret
f
calls to g
, which calls back to f
typedef struct node node;
struct node { int head; node *tail; };
int sum_aux (node *x, int result) {
if (!x) {
return result;
} else {
return sum_aux (x->tail, result + x->head);
}
}
int sum (node *x) {
return sum_aux (x, 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
necessary?
def longList (n:Int) : List[Int] =
if n == 0 then
List (1)
else
val sublist = longList (n - 1)
sublist ::: sublist
tailrec
annotation
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
tailrec
annotation fails if not optimized
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