es6尾递归分析

我的个人网站 www.ryzeyang.top

内容概览
通过chrome浏览器的性能分析工具 performance 分析 es6 的尾递归
20201202082043

尾调用优化

尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

// 情况一     调用函数g之后,还有赋值操作,所以不属于尾调用
function f(x){
  let y = g(x);
  return y;
}

// 情况二  也属于调用后还有操作 所以不属于尾调用
function f(x){
  return g(x) + 1;
}

// 情况三     最后一步为 return undefined;  所以不属于尾调用
function f(x){
  g(x);
}

每次调用函数时,都会产生一个调用栈(会进行一个压栈的操作,栈的特点是先进后出,看看下面的例子),而尾调用是函数的最后一步操作,可以直接用内层函数的调用帧替换外层函数。

function getB(){ //第四步: 执行getB函数中的代码 
    return 'B'
}
function c(a,b){
    return a+b
}
function foo() { //第二步: 执行foo函数中的代码 
 let a=1
 let b=getB();  // 第三步: 调用 getB 函数  getB 进栈
  return c(a , b);  //第五步:调用c函数 c进栈 执行尾调用优化
}
foo(); //第一步:调用foo函数 foo进栈

代码流程浅析:

操作
1. 调用foo函数foo 进栈
2. 执行foo函数中的代码
3. 调用 getB 函数getB 进栈
4. 执行getB函数中的代码
5. getB 函数执行完getB 出栈
6. 调用 c 函数(尾调用)用c的栈帧替换foo的栈帧
7. 执行 c 函数中的代码
8. c 函数执行完c 出栈

注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”

尾递归 (牛!)

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

递归时会容易造成这个stack overflow,但是尝试将代码改成尾递归之后,那个速度就是飞起了。


function Fibonacci (n) {
  if ( n <= 1 ) {return 1};

  return Fibonacci(n - 1) + Fibonacci(n - 2);
}

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};

  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}

console.time("Fibonacci(10) time");
 Fibonacci(10) // 89
console.timeEnd("Fibonacci(10) time");

console.time("Fibonacci(20) time");
Fibonacci(20)
console.timeEnd("Fibonacci(20) time");

console.time("Fibonacci(40) time");
Fibonacci(40)
console.timeEnd("Fibonacci(40) time");

console.log('----------------------------')

console.time("尾递归优化过的Fibonacci2(10) time");
Fibonacci2(10) 
console.timeEnd("尾递归优化过的Fibonacci2(10) time");

console.time("尾递归优化过的Fibonacci2(20) time");
Fibonacci2(20) 
console.timeEnd("尾递归优化过的Fibonacci2(20) time");

console.time("尾递归优化过的Fibonacci2(1000) time");

Fibonacci2(1000) 
console.timeEnd("尾递归优化过的Fibonacci2(1000) time");

通过chrome浏览器的性能分析工具 performance 可以得到下面这张图
Snipaste_2020-07-05_10-33-03

可以明显地看出没有优化过的 Fibonacci 函数 在递归次数为10时 没优化过的 Fibonacci 比使用尾递归优化过的Fibonacci2 还快了0.02ms,超过10之后性能明显下降,左边的红框是Fibonacci(40)的执行结果,要花费2450.614990234375ms 而后边是Fibonacci2(1000)的执行结果 只要0.22412109375ms 这个性能真的差老远了 🙃
通过这个图,还可以明显地看到,右边的红框中Fibonacci2是笔直下来的,即Fibonacci2(1000) 的每次递归消耗的时间是没变的,而左边的这个Fibonacci 会有不同的长度, 说明它每次递归消耗的时间是不同的。
这个直接展示了这个尾递归的优势。(chrome也支持尾递归了,不止safari)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值