I was reading this post on tail recursion.
I’ll copy the posted solution:
unsigned int f( unsigned int a ) {
if ( a == 0 ) {
return a;
}
return f( a - 1 ); // tail recursion
}
I was wondering, what about if the result depends on several recursive function calls?
For example:
unsigned int f( unsigned int a ) {
if ( a == 0 ) {
return a;
}
return f(a -1) + f( a - 1 );
}
Would the code above be optimized by the compiler?
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
rewrite it as follows:
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:Now, tail recursion is applicable. This uses a default value for the multiplicative_accumulator (thanks @Pubby) in order that the first call to
fcan simply bef(x), otherwise you would have to write somethingf(x,1).A couple of final notes thanks to @SteveJessop:
f(a+1)+f(a+1)to2*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.(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 forfloat, where you would probably get small rounding discrepancies. With floating point arithmetic, sometimesa*b*ccan be slightly different fromc*b*a.