关于尾调用和尾递归

1.

(1)尾调用:指某个函数的最后一步是调用另一个函数。
例如:

function a(n){
        return b(n);
    }

(最后一步调用并不意味着在函数的尾部,只要是最后一步即可)

function a(n){
	 if(n>1){
	 return b(n);
	 }
	 return c(n);
 }

(2)什么样的情况不是尾调用

情况一:

function f(x){
  let y = g(x);
  return y;
}

解释:调用g之后,还有赋值操作,故不属于尾调用。

情况二:

function f(x){
  return g(x) + 1;
}

解释:也是在调用g之后,还有后续别的操作,故也不属于尾调用。

情况三:

function f(x){
  g(x);
}

解释:情况三相当于:

function f(x){
    g(x);
    return undefined;
 }

(3)尾调用的优化:(只保留内存函数的调用帧)

《1》:尾调用不同于其他调用的点在于他特殊的调用位置。

《2》:函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call
stack)

《3》:尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了.

《4》:如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。

《5》只有不再用到外层函数的内部变量时,内层函数的调用帧才会取代外层函数的调用帧,否则无法进行尾调用优化。

 function foo(a){
           var one="I'm the first";
           function add(b){
               return b+one;
           };
           return add(a);
       };

备注:上述代码不会进行尾调用优化,因为内层函数用到了外层函数的内部变量。

2.

(1)尾递归:函数调用自身称为递归,如果尾调用自身,则是尾递归。

function tail(n) {
  if (n === 1) return 1;
  return n * tail(n - 1);
}

tail(3);

上述代码的时间复杂度为o(n),最多需要保存n个调用记录;

function tail(n,total){
  if(n===1){
    return total;
    }
    return tail(n-1,n*total);
 }

上述代码的时间复杂度为o(1),只保留一个调用记录.
(2)递归函数的改写:

尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数

备注:参考资料(ES6官方教程)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值