Everyone knows recursion, what about tail recursion? What is tail recursive optimization?

Everyone knows recursion, what about tail recursion? What is tail recursive optimization?

Tang Lei agricultural program code ape stones
Everyone knows recursion, what about tail recursion?  What is tail recursive optimization?
Today, we talk about recursive functions. Why suddenly think of recursion? In fact, I thought of it from the movie name "Horror Cruise" and "Inception". image

What is recursion?

Everyone must have written about recursive functions. In school, it is estimated that the first example is the Fibonacci sequence. E.g:


int Fibonacci(n) {
    if (n < 2) return n;
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}

In short, a recursive function is to call itself "recursively" in a function. When writing a recursive function, the place to pay attention to is the end condition of the recursive function. Using recursive functions can indeed simplify the implementation of many algorithms, such as common binary tree traversal. But often when writing recursive functions, the most likely problem is the so-called "stack overflow".

Why is there a "stack overflow"? Because the process of function calling must use the storage structure of "stack" to save some state at runtime, such as variable copy during function call, address of function call, etc. The "stack" often has limited storage space. When the storage space is exceeded, the famous exception/error "StackOverflowError" will be thrown.

Let's take a simple addition as an example, for example:


int sum(int n) {
    if (n <= 1) return n;
    return n + sum(n-1);
}

std::cout << sum(100) << std::endl;
std::cout << sum(1000000) << std::endl;

Very short answer, after compiling and running, a smaller number can get the correct answer. When the number is enlarged, a "segmentation fault" will happen directly.

What is tail recursion?

I learned about this concept at first because of an interview many years ago. The interviewer asked me "Do you know what tail recursion is?". I thought it was "pseudo" recursion. Could it be false recursion? ? ? At the beginning, I was also in a dazed state (the interviewer was too good at holding back a smile). It can be seen from the word "tail" that if the function calls itself recursively at the tail. The above example is written as tail recursion, it becomes as follows:


int tailsum(int n, int sum) {
    if (n == 0) return sum;
    return tailsum(n-1, sum+n);
}

You can try the result, calculate from 1 to 1000000, it is still a segmentation fault. why? Because of this way of writing, there are essentially multiple levels of function nested calls, and there are still stacks, pops, etc. that occupy storage space (but it can save some space compared with the previous method).

Tail recursion optimization

After you turn on optimization for the compilation options, the time to witness the miracle has arrived, and you can actually calculate the correct result. as the picture shows:
Everyone knows recursion, what about tail recursion?  What is tail recursive optimization?

C++ defaults to segmentation fault. After the compilation optimization is turned on, the result can be calculated normally.

The reason is because the compiler helps to optimize the tail recursion, you can open the assembly code to see (C++ is not shown here). Later, I will use the familiar JVM based language Scala to illustrate this optimization process. (It seems that the Java compiler did not optimize this aspect, at least I experimented my local JDK8 is not there, it is not clear that the latest version is available) (scala itself provides an annotation to help the compiler to force the check whether it can be tail recursion Optimization @tailrec)


object TailRecObject {

   def tailSum(n: Int, sum: Int): Int = {
        if (n == 0) return sum;
        return tailSum(n-1, n+sum);
   }

   def main(args: Array[String]) {
      println(tailSum(100, 0))
      println(tailSum(1000000, 0))
   }

}

The result is shown in the following figure. By default, scalac has done tail recursion optimization and can calculate the result correctly. When the tail recursion optimization is removed by the -g:notailcalls compilation parameter, Exception in thread "main" java.lang.StackOverflowError occurs. Up.
Everyone knows recursion, what about tail recursion?  What is tail recursive optimization?

By default, tail recursion is enabled to optimize normal calculation results, and tail recursion optimization is disabled to "StackOverflow".
Let's take a look at the difference in the generated bytecode.
Everyone knows recursion, what about tail recursion?  What is tail recursive optimization?

Contains bytecode optimized for tail recursion, goto loop directly.
Everyone knows recursion, what about tail recursion?  What is tail recursive optimization?

Disable tail recursion optimized bytecode, method calls.

As can be seen from the above, after the tail recursion is optimized, it becomes a loop (similar to the previous C++).

Well, let's understand the tail recursion here. Personally, we know that there is "tail recursion". Sometimes we write recursion for convenience and good code readability. If it is really for performance reasons, we can implement it in an iterative way. Depends on the specific compiler implementation. Of course, for something like scala, there are some syntactic sugar that can help checksum verification, which is also a good choice. But is it better for us to have the ability to recursively transfer iterations?

Do you want to talk about something next time? Welcome to leave a message. The old rules, if it helps (it’s okay to help other people around you, I won’t see here if you don’t have any help at all), writing an article is really not easy, I hope you can "read it" for more help. Forward sharing support.

Guess you like

Origin blog.51cto.com/15072927/2607568