As it stands, tail recursion doesn't apply. But if you look at the end of the second answer on the question you linked to, you can see how to rewrite the function appropriately. Starting with
unsigned int f( unsigned int a ) {
if ( a == 0 ) {
return a;
}
return f(a-1) + f(a-1);
}
rewrite it as follows:
unsigned int f( unsigned int a ) {
if ( a == 0 ) {
return a;
}
return 2 * f(a-1);
}
Even now, tail recursion still isn't directly applicable. We need to make sure the return is strictly of the form return f(....)
. Rewrite the function again:
unsigned int f( unsigned int a, unsigned int multiplicative_accumulator = 1 ) {
if ( a == 0 ) {
return multiplicative_accumulator * a;
}
return f(a-1, multiplicative_accumulator * 2 );
}
Now, tail recursion is applicable. This uses a default value for the multiplicative_accumulator (thanks @Pubby) in order that the first call to f
can simply be f(x)
, otherwise you would have to write something f(x,1)
.
A couple of final notes thanks to @SteveJessop:
- It was safe to change
f(a+1)+f(a+1)
to 2*f(a+1)
because f has no side effects (printing, modifying the heap, that kind of thing). If f did have side effects, that rewrite wouldn't be valid.
- The original was equivalent to
(2*(2*(2*a))
(or more precisely, (((a+a)+(a+a))+((a+a)+(a+a)))
) whereas the current version is more like (((2*2)*2)*a)
. This is fine, especially for ints, because multiplication is associative and distributive. But this wouldn't be exact equivalent for float
, where you would probably get small rounding discrepancies. With floating point arithmetic, sometimes a*b*c
can be slightly different from c*b*a
.