JS 函数尾递归优化
函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
第 N 个泰波那契数
/**
* @param {number} n
* @return {number}
*/
function tribonacci(n, c1 = 0, c2 = 1, c3 = 1) {
if(n <= 2) return c1 === 0 ? (n+1)>>1 : c3;
return tribonacci(n-1, c2, c3, c1+c2+c3);
};
console.log(tribonacci(0)); // 0
console.log(tribonacci(1)); // 1
console.log(tribonacci(2)); // 1
console.log(tribonacci(4)); // 4
console.log(tribonacci(25)); // 1389537
再举个例子,斐波那契数列的函数尾递归优化
function fibonacci(n , c1=1 , c2=1) {
if( n <= 1 ) {return c2};
return fibonacci(n-1, c2, c1 + c2);
}
fibonacci(100) // 573147844013817200000
fibonacci(1000) // 7.0330367711422765e+208
总结
“尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6 亦是如此,第一次明确规定,所有 ECMAScript 的实现,都必须部署“尾调用优化”。
这就是说,ES6 中只要使用尾递归,就不会发生栈溢出(或者层层递归造成的超时),相对节省内存。