-------------------------by Chenkh-----------------------------
The essay records what the tail recursion is, why it needs tail recursion, and the tail recursive show by example.
0, Preface
Recursion tells the computer what to do through a smart function definition. In functional programming, the use of recursive thinking is ubiquitous. A classic example of recursion:
Recursive fast line def quicksort (Ls:list[int]): list[int] = { if (ls.isempty) ls else quicksort (Ls.filter (_ < Ls.head)::: Ls.head:: Quicksort (Ls.filter (_ > Ls.head)) //quicksort (ls.filter (x = x < Ls.head))::: Ls.head:: Quicksort (ls.filter (x = x > Ls.head)) }
We take the last quick sort function of the above code as an example, using recursion, its code implementation is very concise and easy to understand. The core of recursive function is to design the recursive expression, and to determine the boundary condition of the algorithm. In the quick sort above, it is assumed that an empty list is a list of good orders, which is the recursive boundary condition, which is the sign of recursive termination.
1, what is tail recursion?
The tail recursion is still a recursive in nature and can be regarded as an improvement of the common recursive method.
Tail recursion means that the recursive call is the last statement of the function, and the result is returned directly, which is a special kind of recursive invocation. Because recursive results are always returned directly, the tail recursion is easier to convert to a loop , so the compiler can easily optimize it.
2, why do I need the tail recursion?
Recursive algorithms need to keep the call stack low, inefficient, and run out of memory or stack overflow if the number of calls is higher. However, the tail recursion can overcome this shortcoming.
3,show by factorial
recursive factorial def factorial (n:bigint): BigInt = { if (n <=1) 1 Else n * factorial (n-1) } //tail recursive factorial def facto Rialtailrecursive (n:bigint): BigInt = { def _loop (Acc:bigint, n:bigint): BigInt = if (n <=1) ACC else _loop (acc*n , n-1) _loop (1, N) }
/* we found that because each recursive invocation of N-1 's factorial, there is an additional multiplication calculation, which makes the data in the stack need to be preserved. The new function stack is to be allocated in the new recursion. factorial (4) -------------- 4 * factorial (3) 4 * (3 * factorial (2)) 4 * (3 * (2 * factorial (1))) 4 * (3 * (2 * 1)) and the tail recursion is equivalent in efficiency and loop, the _loop in the function is the last step, either returning the value of the recursive boundary condition, or invoking the recursive function itself. factorialtailrecursive (4) -------------------------- _loop (1, 4) _loop (4, 3) _loop (12, 2) _loop (1) */
The key to rewriting a recursive version of a tail:
Tail recursive version The most important thing is to find the appropriate accumulator, the accumulator can keep the last recursive call left on the stack of data, accumulate the results of the previous call, so that the stack data can be discarded, the current function stack can be reused.
In this example, the variable ACC is the accumulator, and each recursive call updates the variable until the recursive boundary condition is satisfied.
For tail recursion, the Scala language specifically adds a comment that @tailrec
ensures that the program written by the programmer is the correct tail-recursive program, and that if it is not a tail-recursive program written out inadvertently, the compiler will report a compilation error reminding the programmer to modify their code.
scala-tail recursion