Let's take above example and set n to a large number such as Then run the program again and a StackOverflowError will happen. In this case, recursion traditional recursion may be considered as a bad idea as it will make the program fail unexpectedly.
In fact, there is another recursion pattern which can be adopted to resolve the StackOverflowError -- Tail recursion. A tail recursion is also a kind of recursion but it will make the return value of the recursion call as the last statement of the method. This will make the calculation occurs before the recursion call and hence there is no need to keep the stack to store the intermediate value when it moves to the next recursive call.
One big change in this method is there is a parameter sum in the method signature, this sum will store the calculated result at each recursive call. So this value can be directly returned when the recursion call stops.
If n is set to and run the tail recursion version method, there will be no StackOverflowError now. In simple, the main difference between the traditional recursion and tail recursion is when the actual calculation takes place.
In traditional recursion, calculation will happen after the recursion call while the calculation will occur before the recursion call in tail recursion. But, and here comes the nice part of using your own structure instead of the call stack, you're not always needed to keep the whole stack to continue computations:.
But to conclude with a nice touch of "know the problem before trying to implement it" unreadable, hard to debug, hard to visually proove, it's bad code, but it's fun :. If you ask me, the best implementation is the more readable one for the Fibonacci example, probably the one with an LRU cache but by changing the Stack Overflow for Teams — Collaborate and share knowledge with a private group.
Create a free Team What is Teams? Collectives on Stack Overflow. Learn more. Why is tail recursion optimization faster than normal recursion in Python?
Ask Question. Asked 5 years, 5 months ago. Active 5 years, 5 months ago. Viewed 4k times. Here is the code I'm running: from contextlib import contextmanager import time Timing code from StackOverflow most likely.
My guess is the overhead with creating entries on the stack, but I'm not sure how to find out. Edit: In playing with call counts, I made a loop to try both at various num values. Improve this question.
Joe Joe 2, 17 17 silver badges 20 20 bronze badges. Probably it comes down to the fact that it has to allocate just one stack frame instead of multiple ones. It may even benefit from the fact that probably the allocator is returning the same block at each iteration for the new frame object, so it has better cache locality. Switching call order affects it slightly. Between 20 and 40, depending on call order, they are equal-ish. So it does seem to be stack overhead related. If I time these calls with the timeit module, the tail call optimized version wins for low repetition counts, but the other version wins for high repetition counts.
The stack frame allocation hypothesis doesn't seem to hold up under further scrutiny. Does timeit run multiple times to generate a value? I need to break these into separate files and mess around a little more. The setup and break down as well as the run count of the timing mechanism used might have as much influence as the actual execution. What is tail recursion? A recursive function is tail recursive when a recursive call is the last thing executed by the function.
Attention reader! The tail recursive functions considered better than non tail recursive functions as tail-recursion can be optimized by the compiler. Compilers usually execute recursive procedures by using a stack. This stack consists of all the pertinent information, including the parameter values, for each recursive call.
When a procedure is called, its information is pushed onto a stack, and when the function terminates the information is popped out of the stack. Thus for the non-tail-recursive functions, the stack depth maximum amount of stack space used at any time during compilation is more. Can a non-tail recursive function be written as tail-recursive to optimize it? Consider the following function to calculate the factorial of n.
It is a non-tail-recursive function. Although it looks like a tail recursive at first look. If we take a closer look, we can see that the value returned by fact n-1 is used in fact n , so the call to fact n-1 is not the last thing done by fact n. Python3 A NON-tail-recursive function. The idea is to use one more argument and accumulate the factorial value in the second argument.
0コメント