In program design, Recursion is a common concept. Reasonable Use of Recursion can improve the readability of the code, but it may also cause some problems.
The following uses Factorial as an example to illustrate recursive usage. The implementation language is PHP:
<? Php
Function factorial ($ n ){
If ($ n = 0 ){
Return 1;
}
Return factorial ($ n-1) * $ n;
}
Var_dump (factorial (100 ));
?>
If XDebug is installed, the following error may occur:
Fatal error: Maximum function nesting level of '000000' reached, aborting!
Note: This is a protection mechanism of XDebug, which can be set through the max_nesting_level option.
Even if the code runs normally, as long as we increase the number of parameters, the program will report an error sooner or later:
Fatal error: Allowed memory size... Bytes exhausted
Why? To put it simply, recursion causes stack overflow. There are several ways to avoid this problem, such as using Tail Call to eliminate the impact of recursion on stack.
The following uses Lua as the description language to describe the meaning of the End call. The Code is as follows:
Function factorial (n)
If (n = 0) then
Return 1
End
Return factorial (n-1) * n
End
Print (factorial (100 ))
This Code also encounters stack overflow. How can we use the end call? Let's take a look at the definition of the final call: If a function does not do anything else after it executes the callback function call, it is called the final call. The image point is to directly return a function call. The End call does not return the original function, so no additional stack is required to retain the data of the called function. After the code above is changed to the end of the call, it looks like the following code:
Function factorial (n, accumulator)
Accumulator = accumulator or 1
If (n = 0) then
Return accumulator
End
Return factorial (n-1, accumulator * n)
End
Print (factorial (100 ))
Note: For more information about Lua Tail call, see Proper Tail Recursion.
We use PHP to implement a factorial of the called version at the end:
<? Php
Function factorial ($ n, $ accumulator = 1 ){
If ($ n = 0 ){
Return $ accumulator;
}
Return factorial ($ n-1, $ accumulator * $ n );
}
Var_dump (factorial (100 ));
?>
It is a pity that PHP does not support end-to-end calls only after testing! Fortunately, there is no way to survive. After carefully reading the introduction to the end call in Wikipedia, you will find that the concept of Trampoline is mentioned. To put it simply, we use higher-order functions to eliminate recursion. Based on this theory, we can rewrite the above Code called at the end to the following method:
<? Php
Function factorial ($ n, $ accumulator = 1 ){
If ($ n = 0 ){
Return $ accumulator;
}
Return function () use ($ n, $ accumulator ){
Return factorial ($ n-1, $ accumulator * $ n );
};
}
Function trampoline ($ callback, $ params ){
$ Result = call_user_func_array ($ callback, $ params );
While (is_callable ($ result )){
$ Result = $ result ();
}
Return $ result;
}
Var_dump (trampoline ('factorial ', array (100 )));
?>
It looks good, but I have to apologize to everyone. This article uses Recursion TO IMPLEMENT factorial. In fact, we only need to use a loop, code Daquan specifically mentions this point:
<? Php
Function factorial ($ n ){
$ Result = 1;
For ($ I = 1; $ I <= $ n; $ I ++ ){
$ Result * = $ I;
}
Return $ result;
}
Var_dump (factorial (100 ));
?>
There are also many other methods to avoid stack overflow caused by recursion. For example, in Python, the last call can be eliminated through the decorator and exception, which makes people feel a different kind:
Tail Call Optimization Decorator (Python recipe)
In addition, the blog post by the father of Python on why Python does not support end-to-end calling is also very interesting:
Tail Recursion Elimination
Final Words on Tail CILS
Well, write it here. There is no need to use recursion unless it can improve code readability. It is best to consider using technologies such as Tail Call or Trampoline to avoid potential stack overflow.
Author: Lao Wang