The strict tail recursion definition is not useless at all.
Let's say that it's only useful if the language (or the
implementors of the language) decide to make it so. Formally
(and in C++), the definition Andrey gave is more useful: if your
code conforms to his contraints, it can potentially be optimized
to eliminate the recursion; if it doesn't, then eliminating the
recursive call will require implementing some sort of stack
("recursion") manually, which defeats the space optimization.
Potentially; whether a compiler does so or a language requires
it is another question.
It is used by many functional programmers. This is an
important concept to avoid stack overflow / boost performances
as most compilers will implement TCE, and I mean true TCE,
there are optimizations that transform non- tail recursions to
tail ones before applying TCE, but less compilers do it.
I think in some languages, the language specification requires
tail recusion optimization. Since identifying what you call
tail recursion is trivial, both from a formal and from a
practical point of view, it's not unreasonable for a language to
require it. Since identifying all possible tail recursions
(using the wider definition) is far from trivial (and may even
be undecidable), no language will ever require it. Somewhere
between the two, who knows.
That's why functional programmers use techniques like the one
given by Danny Woods.
One would hope it was only used when the language didn't support
looping otherwise. In such cases, you *must* have a guarantee
that the optimization is applied.
Since we are on a C++ newsgroup, let's talk about C++
compilers and their behavior when confronted with the
factorial recursive function :
I suspect most don't even check for it in the most trivial
cases. In C++, you have very powerful looping constructs, and
it isn't idiomatic to use recursion (tail or otherwise) when
there is a simple solution using a loop. So typically, the
compiler is just wasting time checking for it.
Also, as I mentionned elsewhere, the presence of non-trivial
destructors which must be called *after* the return value has
been copied out adds to the complexity, and reduces the
probability that the optimization could be applied.
int fac (int n)
{
if (n==0)
return 1;
else
return fac(n-1) * n;
}
VC++9 won't apply TCE while GCCv4 will.
It would be interesting to see if there are any cases of
compilers which apply TCE only for what you call tail recursion,
and not in this case.
C++ is not a functional language. It has different idioms. And
compiler optimization should be tuned to the common idioms of
the language it is optimizing. A compiler for Scheme, or any of
the other Lisp dialects, would be seriously deficient if it
didn't apply TCE. In a compiler for C++, on the other hand, I'd
say that checking whether TCE could be applied is a waste of
time. The only time you'd write factoriel like that in C++
would be as a demonstration of recursion; in normal code, you'd
write the iterative solution directly.
But if, as a functional programmer, we use an accumlulator to
obtain TRUE tail recursion (from the academic definition) :
int fac (int n, int acc)
{
if (n==0)
return acc;
else
return fac(n-1, acc*n );
}
then both VC++ and GCC will apply TCE.
Interesting. I'd have expected neither. (On the other hand,
Sun CC also does both, so maybe the compiler writers have found
a justification I'm unaware of.)
And in functional languages, the situation is more chaotic.
The only sure way to get TCE is to implement *true* tail
recursion.
The only sure way in some functional languages, probably.
There's no sure way in C++. The way to do this in C++ is to
write whatever is most natural, without worrying about whether
the compiler will find tail recursion or not, and then, if the
profiler shows it's too slow, optimize manually (probably
eliminating the recursion).