Most comments: https://news.ycombinator.com/item?id=1540567 (357 points, 1555 days ago, 60 comments)
Latest: https://news.ycombinator.com/item?id=6823768 (2 points, 329 days ago, 3 comments)
Also, it would be a good idea to add [2010] to the title because some of the optimizations may be available in the latest gcc version. (I'd like to read an update, or a year by year comparison.)
Tail-recursions are the exact equivalent of loops.
We've lost sight of what "recursion" means. There's a difference between recursive functions and recursive algorithms, which need by definition a stack and there are lots of such problems that can't be optimized to run in constant space. Whether that stack is the call stack, or a manually managed stack (within a loop or a tail-recursion), that doesn't matter, what matters is that the space requirements are definitely not constant.
It's nice that the GCC compiler can optimize that Fibonacci sample. However you're not getting a guarantee that it can optimize every function that is optimizable. I'm also pretty sure that the compiler is extremely limited in what it can do here. And that is dangerous, as running in constant space is often a matter of correctness, not one of optimization. I would rather have the compiler err on me if it sees non-tail recursive calls, rather than try to be smart sometimes.
Your vocabulary is not standard.
https://www.google.com/webhp?#q=recursive+algorithm
http://en.wikipedia.org/wiki/Recursion_(computer_science)
A recursive algorithm is one that computes a result by way of the same algorithm applied to a smaller input. (Smaller, or else the algorithm will diverge.)
The distinction between "stack" and "parameter" is a minor one. A simple factorial function has an ever-growing (accumulating) parameter.
Tail-recursive algorithms can be translated into loop algorithms. Thet are still recursive.
This is not true. The first example that comes to mind is the ridiculously over-powered one: conjecturally (https://en.wikipedia.org/wiki/Collatz_conjecture), the definition
f x | x `mod` 2 == 0 = f x/2
f x | x `mod` 2 == 1 = f $ 3*x - 1
f 1 = [ whatever ]
is perfectly well defined on positive integers `x`, although many recursive calls actually apply `f` to larger values.(To be fair, you didn't actually say what 'smaller' meant. I think it's one of the standard term-rewriting theorems that, if given the freedom to define 'smaller' appropriately, then any total recursive function does make only smaller recursive calls.)
I've sometimes wondered why OCaml has "let" and "let rec", but not "let tail rec". It seems that something like this would be good for documentation and avoinding errors.
Not necessarily. In the common case of a procedure calling itself a loop does wind up being equivalent. OTOH with mutually recursive procedures (two or more procedures which call each other), there is no straightforward way to convert the tail calls into a loop.
while (condition)
{
a();
if (!condition)
break;
b();
}Of course adding a unit test for every function saved the day as usual, however it is trivial to convert tail recursion into a loop and not worry about stack overflow.
But if you call a function, even without suppling s as a parameter, instead of simply result += s[i], then that function could have side effects on the string since the char *s is not "restrict".
http://goo.gl/PgqAIs <-- The code
My intuition seems to fail me, though, when we get to things like escape analysis in languages like Java. It seems like a conceptually simple problem - that is, stack allocating or "unpacking" objects (like value classes/objects)that are __obviously__ going to not escape the scope of a method. I don't mean simple as in determining all objects. And the __obvious__ ones seem much more common than the non-obvious ones, in regular code. But then it seems that this is a fairly recent implementation, which might suggest that it isn't so simple to implement?
(It might have got better now - I know the OpenJDK folks were making some efforts in that direction)