递归尾调用优化

以前分享过递归,其中有一个优化就是尾调用。编程

先明确尾调用的概念:bash

尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是return调用另外一个函数。函数式编程

function fn() {  

  return gn()

}
复制代码

下面几种不是尾调用:函数

function fn() {  

  return gn() + 10//调用以后又赋值

}

function fn() {  

  gn()//没有return,或者说是return了undefined

}

function fn() {  

  var gnn = gn();  

  return gnn//调用后还有操做

}
复制代码

注意,是最后一步操做,不是放在最末尾,下面也算尾调用:优化

function fn(a) {  

  if(a > 10){ 

   return gn(10)  

  }else{    

    return gn(20)  

  }  

  return gn()

}

复制代码

以前分享过调用栈,若是不是尾调用,那么会生成一个调用栈,直到栈顶的执行完毕,才会释放以前造成的调用栈的内存。尾调用由于是最后一步操做,因此不须要保留以前的栈,也就不须要保存以前的内存,就是递归里面计算阶乘那两个函数。ui

注意,并非全部的函数都能尾调用优化,要看你这个函数需不须要使用某些上个函数的变量或者什么的。spa

尾调用优化其实很大一部分就是递归函数在使用,由于递归函数调用的时候很是耗费内存,可能须要保存成百上千调用栈,很容易内存溢出。若是是尾递归就只有一个调用栈,能把复杂度O(n)的变成O(1)。code

至于怎么改写递归变成可使用尾调用就比较复杂了,须要根据不一样函数去修改。好比阮一峰大神的例子:cdn

function sum(x, y) {

  if (y > 0) {

    return sum(x + 1, y - 1);

  } else {

    return x;

  }

}

sum(1, 100000)
复制代码

改为:递归

function sum(x, y) {

  if (y > 0) {

    return sum.bind(null, x + 1, y - 1);

  } else {

    return x;

  }

}
复制代码

而后使用蹦床函数:

function trampoline(f) {

  while (f && f instanceof Function) {

    f = f();

  }

  return f;

}
复制代码

执行:

trampoline(sum(1, 100000))
复制代码

你会发现,不少递归函数都能改为相似的,而后使用蹦床函数实现尾调用优化。

而ES6对尾调用有什么优化?就是函数默认值,在一些场景下,好比阶乘的递归,采用默认值实现尾递归优化。

相关文章
相关标签/搜索