Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

尾调用 #9

Open
39Er opened this issue May 11, 2017 · 0 comments
Open

尾调用 #9

39Er opened this issue May 11, 2017 · 0 comments

Comments

@39Er
Copy link
Owner

39Er commented May 11, 2017

尾调用

在函数的执行过程中,如果最后一个动作是一个函数的调用,即这个调用的返回值被当前函数直接返回,则称为尾调用

如:

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

而如下则不是尾调用:

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

原因是它的最后一步操作是将 g 函数调用的返回值和 1 进行加法操作,而不是调用其他函数,所以它不是尾调用。

尾调用的重要性:它不会在调用栈上增加新的堆栈帧,而是直接更新调用栈,调用栈所占空间始终是常量,节省了内存,避免了爆栈的可能性。

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

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

尾递归

    顾名思义,在一个尾调用中,如果函数最后的尾调用位置上是这个函数本身,则被称为尾递
归。递归很常用,但如果没写好的话也会非常消耗内存,导致爆栈。但是对于尾递归来说只
存在一个调用栈,便永远不会发生“栈溢出”错误。
就以求一个给出数的阶乘来探索吧。在传统的做法中利用n的递减乘上原函数,这样复杂度
便会很高,数据量一大便会发生“栈溢出”的错误了。

传统的解决办法

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

尾调用

function a(n, t) {
  if(n === 1) {
	return t;
  }
  return a(n - 1, n * t)
}

对比两个解决办法的复杂度就知道,尾调用只保留了一个记录。

尾递归优化

上面的尾递归方法虽然可以避免发生“栈溢出”的错误,但只是一个普通的阶乘方法,传递两个参数,第二个参数不容易让调用者理解,可读性差,优化方式有两种

  1. 柯里化:将多参数函数转换成单参数函数

     function currying(fn,n){
     	return function(m){
     		fn.call(this,m,n);
     	}
     }
     const b = currying(a,1);
     b(n);
    

2、使用ES6初始化参数默认值特性

    function a(n,t=1){
		if(n === 1) {
			return t;
		}
		return a(n - 1, n * t)
	}

参考文章:阮老师的这篇文章
    还有:https://www.keephhh.com/2017/05/02/tc/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant