For different types of problems that require repeated computation, the loop and recursive methods have their own advantages and provide a more intuitive and simple solution. The following describes the recursion and loop of JavaScript in detail, interested friends can learn more
Recursion and loop
For different types of problems that require repeated computation, the methods of loop and Recursion have their own advantages and provide a more intuitive and simple solution. On the other hand, loops and recursive methods can convert each other. Code in any loop can be rewritten recursively to implement the same function, and vice versa. Without losing its universality, loop and Recursion can be summarized by the following pseudocode.
Pseudocode format description: The loop uses the while form; variables are not defined; values are assigned with: =; both conditional expressions and executed statements are written as functions, and related values are written in parentheses. For other syntaxes, try to be close to the Javascript specification.
The Code is as follows:
// Pseudo code of a loop
// While format
Function loop (arguments ){
// Initial value of the result
Result: = initial_value;
While (condition (variable, arguments) {// The loop condition. You may only need arguments, or you may want to introduce cyclic variables for convenience.
// Calculation result. Parameters include previous results, current cyclic variables, and external variables.
Result: = calculate (result, variable, extern_variables );
// Modify the external environment of the function.
ChangeStatus (result, variable, extern_variables );
// After the statement in the loop body is executed, modify the parameter or loop variable.
Modify_arguments_variable (arguments, variable );
}
// Return results
Return result;
}
We also provide pseudo code for recursive functions.
The Code is as follows:
// Pseudo code of a recursion
Function recursion (arguments ){
// The following code controls the structure of repeated function calls.
// Obtain the new parameter that calls this function again, which may be multiple sets of arguments values.
// Corresponds to the condition (variable, arguments) and modify_arguments_variable (arguments, variable) in the loop ).
New_arguments: = conditional_get_next (arguments );
// Call the function itself for each group of new parameters.
Results: = recursion (new_arguments );
// The following code indicates the function that is run every call.
// Calculation result. Previous results, current cyclic variables, and external variables are involved.
// Corresponds to result: = calculate (result, variable, extern_variables) in the loop ).
Result: = calculate (arguments, extern_variables );
Result: = combine (result, results );
// Modify the external environment of the function.
ChangeStatus (result, arguments, extern_variables );
Return result;
}
By comparing the two sections of code, we can see that the loop and Recursion have similar components. By changing the order and appropriate transformation, any loop can be implemented by recursion. When the program is simple, this conversion is easy to see. For example, the following simple aggregate sum function:
The Code is as follows:
// Loop
Function sum (num ){
Var result = 1;
While (num> 1 ){
Result + = num;
Num --;
}
Return result;
}
Corresponding recursive form:
The Code is as follows:
// Recursion
Function sum2 (num ){
If (num> 1 ){
Return num + sum (num-1 );
} Else {
Return 1;
}
}
On the contrary, most recursive Programs can be implemented directly by loops. The following is a function in the cyclic form of finding the maximum common approx.
The Code is as follows:
Function gcd2 (a, B ){
Var temp;
If ( Temp =;
A = B;
B = temp;
}
Var c = a % B;
While (c! = 0 ){
A = B;
B = c;
C = a % B;
}
Return B;
}
However, the conversion from recursion to loop is not so easy. The new parameter section in recursive pseudo-code that generates and calls this function again
New_arguments: = conditional_get_next (arguments );
It is more flexible than the corresponding part of the loop. Recursion can be divided into two types according to the number of new parameter groups (all parameters required by the function are a group. The first type is a fixed number of parameter groups. This recursion can be converted to a loop, for example, an example of the Fibonacci series and the maximum common number; the second type is that the number of parameter groups is uncertain-as in traversing a graph or tree, each vertex has any adjacent vertex-This recursion cannot be directly converted to a loop.
Because loops can only be repeated in one dimension, recursion can traverse two-dimensional structures. For example, in a tree, a node has both its subnodes and nodes of the same level. A simple one-dimensional loop cannot traverse in two directions.
However, if we remember some information about the node location through a certain data structure in the loop, the second type of recursion can also be implemented in a loop.
Let's use another example to practice the conclusions observed above. HTML5 defines a new method getElementsByClassName (names) for Document and Element to return all elements with the given class value. Some browsers including Firefox3 support this method. Next we will first use the recursive method to give a version with a weak function, and then rewrite it with a circular method.
The Code is as follows:
Var getElementsByClass = {};
// Elem is an HTMLElement
// Name is the name of a single class
// Returns an array of elements containing the given name in all class attributes of elem.
GetElementsByClass. recursion1 = function (elem, name ){
Var list = [];
Function getElements (el ){
If (el. className. split (''). indexOf (name)>-1 ){
List. push (el );
}
For (var I = 0, c = el. children; I GetElements (c [I]);
}
}
GetElements (elem );
Return list;
}
As mentioned above, in order to remember the node location information in a loop, we need a data structure that can implement the following methods.
Push (object) // write an object.
Objectpop () // read the recently written object and delete it from the data structure.
Objectget () // read the recently written object without changing the data structure.
The stack is such a post-import, first-out data structure. The Array object in Javascript supports the first two methods. We can add a third method to it.
Loop version:
The Code is as follows:
GetElementsByClass. loop1 = function (elem, name ){
// Use a js array as the basis of a needed stack
Var stack = [];
Stack. get = function (){
Return stack [stack. length-1];
}
Var list = [];
// The business logic part. put the eligible element to the list.
Function testElem (el ){
If (el. className. split (''). indexOf (name)>-1 ){
List. push (el );
}
}
// Check the root element
TestElem (elem );
// Initialize the stack
Stack. push ({
Pointer: elem,
Num: 0
});
Var parent, num, el;
While (true ){
Parent = stack. get ();
El = parent. pointer. children [parent. num];
If (el) {// enter a deeper layer of the tree
TestElem (el );
Stack. push ({
Pointer: el,
Num: 0
});
}
Else {// return to the upper layer
If (stack. pop (). pointer === elem ){
Break;
}
Else {
Stack. get (). num + = 1;
}
}
}
Return list;
}
To sum up. All loops can be implemented recursively; All recursion can be implemented cyclically. Which method is used is more convenient and intuitive based on the specific ideas and users' preferences.
Efficiency
In terms of performance, recursion is no better than loop. In addition to the overhead of multiple function calls, recursion may lead to unnecessary Repeated Computation in some cases. Take the recursive program used to calculate the Fibonacci sequence as an example. When the nth item A (n) is obtained, each item is calculated repeatedly from the nth N-2. The smaller the number of items, the more repeated. If B (I) is the number of times that item I is calculated
B (I) = 1; I = n, n-1
B (I) = B (I + 1) + B (I + 2); I
In this way, B (I) forms an interesting inverse Fibonacci series. When A (n) is used:
B (I) = A (n + 1-i)
From another perspective, if C (I) is the number of addition times required for A (I ),
C (I) = 0; I = 0, 1
C (I) = 1 + C (I-1) + C (I-1); I> 1
So that D (I) = C (I) + 1 has
D (I) = 1; I = 0, 1
D (I) = D (I-1) + D (I-1)
Therefore, D (I) forms another Fibonacci series. Therefore, we can conclude that:
C (n) = A (n + 1)-1
While A (n) increases in Geometric Order, this excess repetition will become amazing when n is large. Corresponding procedures that adopt loops include:
B (n) = 1; n is an arbitrary value.
C (n) = 0; n = 0, 1
C (n) = n-1; n> 1
Therefore, when n is large, the previous circular program is much faster than the recursive program.
Like the loop in the previous section, this defect in recursion can also be compensated. We only need to remember the items that have been computed. When a large item is obtained, we can directly read the previous items. This technology is common in recursion and is called "memorization ).
The following is a recursive algorithm for finding the Fibonacci series using storage technology.
The Code is as follows:
// Recursion with memorization
Function fibonacci4 (n ){
Var memory = []; // used to store each calculated item
Function calc (n ){
Var result, p, q;
If (n <2 ){
Memory [n] = n;
Return n;
}
Else {
P = memory [n-1]? Memory [n-1]: calc (n-1 );
Q = memory [n-2]? Memory [n-2]: calc (n-2 );
Result = p + q;
Memory [n] = result;
Return result;
}
}
Return calc (n );
}