What is a recursive function/method?
Any method can call other methods or itself. When this method calls itself, we call it a recursive function or a recursive method.
Recursion has two features::
1. Recursive methods always call themselves until certain conditions are met
2. the recursive method has some parameters, and it will pass some new parameter values to itself.
So what is a recursive function? There is no essential difference between a function and a method, but a function is only used within a class. In the past, only methods in C # Had anonymous functions starting from. NET 3.5.
Therefore, we 'd better call it a recursive method instead of a recursive function. In this article, we will call it a recursion.
Why do we use recursion in applications? When to use recursion? How to use it?
"Writing a program can be expressed by a value assignment or an if-then-else statement, while a while statement can be expressed by a value assignment, if-then-else, or recursion ." (From Ellis Horowitz's Data Structure BASICS (C)-Fundamentals of Data Structure in C)
Recursive solutions are very convenient and powerful for complex development. However, frequent use of call stacks may cause performance problems (sometimes poor performance ).
Let's take a look at the figure below:
Call Stack illustration
Below I will introduce some examples to help you better understand the risks and returns of recursion.
1. factorial
Factorial (!) Is the product of all positive integers smaller than a certain number.
0! = 1
1! = 1
2! = 2*1! = 2
3! = 3*2! = 6
...
N! = N * (n-1 )!
The following is an implementation method for calculating factorial (without recursion ):Copy codeThe Code is as follows: public long Factorial (int n)
{
If (n = 0)
Return 1;
Long value = 1;
For (int I = n; I> 0; I --)
{
Value * = I;
}
Return value;
}
The following uses recursive methods to calculate factorial, which is more concise than the previous code.Copy codeThe Code is as follows: public long Factorial (int n)
{
If (n = 0) // the condition that limits the call of this method
Return 1;
Return n * Factorial (n-1 );
}
As you know, the factorial of n is actually the factorial of N-1 multiplied by n, and n> 0.
It can be expressed as Factorial (n) = Factorial (n-1) * n
This is the return value of the method, but we need a condition
If n = 0, 1 is returned.
Now the logic of this program should be clear, so that we can easily understand it.
2. Fibonacci Series
The Fibonacci series are numbers in the following order:
, 55 ,... If F0 = 0 and F1 = 1 then Fn = Fn-1 + Fn-2
The following method is used to calculate Fn (no recursion, good performance)Copy codeThe Code is as follows: public long Fib (int n)
{
If (n <2)
Return n;
Long [] f = new long [n + 1];
F [0] = 0;
F [1] = 1;
For (int I = 2; I <= n; I ++)
{
F [I] = f [I-1] + f [I-2];
}
Return f [n];
}
If we use the recursive method, this code will be simpler, but the performance will be poor.Copy codeThe Code is as follows: public long Fib (int n)
{
If (n = 0 | n = 1) // The condition is met.
Return n;
Return Fib (k-2) + Fib (k-1 );
}
<STRONG> <SPAN style = "FONT-SIZE: medium"> 3. boolean combination </SPAN> </STRONG>
Sometimes the problem we need to solve is much more complicated than the series of the Fibonacci, for example, we need to enumerate all the combinations of Boolean variables. In other words, if n = 3, we must output the following results:
True, true, true
True, true, false
True, false, true
True, false, false
False, true, true
False, true, false
False, false, true
False, false, and false: If n is large, it is difficult to solve this problem without recursion.Copy codeThe Code is as follows: public void CompositionBooleans (string result, int counter)
{
If (counter = 0)
Return;
Bool [] booleans = new bool [2] {true, false };
For (int j = 0; j <2; j ++)
{
StringBuilder stringBuilder = new StringBuilder (result );
StringBuilder. Append (string. Format ("{0}", booleans [j]. ToString (). ToString ();
If (counter = 1)
Console. WriteLine (stringBuilder. ToString ());
CompositionBooleans (stringBuilder. ToString (), counter-1 );
}
}
Now let's call the above method.:Copy codeThe Code is as follows: CompositionBoolean (string. Empty, 3 );
Ian Shlasko recommends that we use recursion like this.:Copy codeThe Code is as follows: public void BooleanCompositions (int count)
{
BooleanCompositions (count-1, "true ");
BooleanCompositions (count-1, "false ");
}
Private void BooleanCompositions (int counter, string partialOutput)
{
If (counter <= 0)
Console. WriteLine (partialOutput );
Else
{
BooleanCompositions (counter-1, partialOutput + ", true ");
BooleanCompositions (counter-1, partialOutput + ", false ");
}
}
4. Internal retrieval exception
If you want to obtain innerException, select the recursive method, which is useful.Copy codeThe Code is as follows: public Exception GetInnerException (Exception ex)
{
Return (ex. InnerException = null )? Ex: GetInnerException (ex. InnerException );
}
Why should we get the last innerException ?! This is not the topic of this Article. Our topic is that if you want to obtain innerException in the innermost part, you can do it by using recursive methods.
The code here:Copy codeThe Code is as follows: return (ex. InnerException = null )? Ex: GetInnerException (ex. InnerException );
It is equivalent to the following code: Copy codeThe Code is as follows: if (ex. InnerException = null) // Constraints
Return ex;
Return GetInnerException (ex. InnerException); // call yourself with an internal exception as a parameter
Now, once we get an exception, we can find the innerException in the innermost part. For example:Copy codeThe Code is as follows: try
{
Throw new Exception ("This is the exception ",
New Exception ("This is the first inner exception .",
New Exception ("This is the last inner exception .")));
}
Catch (Exception ex)
{
Console. WriteLine (GetInnerException (ex). Message );
}
I used to write an article about anonymous recursive methods, but I found that my explanation cannot go beyond that article.
5. Search for files
I used recursion in the demo project for you to download. With this project, you can search for a path and obtain the paths of all the files in the current folder and Its subfolders.Copy codeThe Code is as follows: private Dictionary <string, string> errors = new Dictionary <string, string> ();
Private List <string> result = new List <string> ();
Private void SearchForFiles (string path)
{
Try
{
Foreach (string fileName in Directory. GetFiles (path) // Gets all files in the current path
{
Result. Add (fileName );
}
Foreach (string directory in Directory. GetDirectories (path) // Gets all folders in the current path
{
SearchForFiles (directory); // The methods callitself with a new parameter, here!
}
}
Catch (System. Exception ex)
{
Errors. Add (path, ex. Message); // Stores Error Messages in a dictionary with path in key
}
}
This method does not seem to meet any conditions, because if each directory has no sub-directories, all sub-files are automatically traversed.
Summary
We can actually use recursive algorithms to replace recursion with better performance, but we may need more time overhead and non-recursive functions. But the key is that we must select the best implementation method based on the scenario.
Dr. James MaCaffrey believes that recursion is not recommended unless there is no way. You can read his article.
I think:
A) if performance is very important, avoid recursion.
B) if the recurrence method is not complex, avoid recursion.
C) if neither A nor B is satisfied, do not hesitate to use recursion.
For example:
Section 1 (factorial): recurrence is not complex, so recursion is avoided.
Section 2 (Fibonacci): recursion like this is not recommended.
Of course, I am not trying to belittle the value of recursion. I remember that the important chapter in artificial intelligence contains a Minimax algorithm, which is all implemented by recursion.
But if you decide to use the team rules method, you 'd better try to use storage to optimize it.
Copyright Disclaimer: This article was originally prepared by Tony Qu. This statement must be retained without the author's consent, and the original article connection is clearly provided on the article page. Otherwise, it is deemed as infringement.