尾调用优化
来源:互联网 发布:玲珑密保网络错误 编辑:程序博客网 时间:2024/05/21 17:27
什么是尾调用?
尾调用是函数式编程的重要概念之一,即在某个函数的最后一步来调用另一个函数。
function f(x){ return g(x);}
而除此之外的其他写法和形式都不是尾调用。比如说:
// 情况一function f(x){ let y = g(x); return y;}// 情况二function f(x){ return g(x) + 1;}// 情况三function f(x){ g(x);}
尾调用之所以被称为尾调用,就是因为它的调用位置比较特殊。
还记得我们初学js函数部分吧,函数名保存在栈内存,函数值保存在堆内存中。函数调用会产生一个‘调用记录’,用来保存调用位置和内部变量等信息。也就是说,在函数A内部调用函数B,那么在函数A的调用帧上方,还会形成一个B的调用帧。等B运行结束,将结果返回给了A,B的调用帧才消失。
当然了,如果函数B内部调用了函数C,那么B的上方也会有函数C的调用帧。以此类推,从而形成了‘调用栈’(call stack)。
但是当嵌套调用比较多的时候,比如说递归的时候,会造成相当多的调用帧。
所以我们需要用到尾调用优化,来减少调用帧的使用。尾调用因为在函数的最后一步,所以不需要保留外层函数的调用帧。
例子:
function f(){ let m = 1; let n = 2; return g(m + n);}f();// 等同于function f(){ return g(3);}// 等同于g(3);
在上面的代码中,如果函数 g 不是尾调用的话,那么函数 f 就不能结束,而应该去保存内部变量m和n的值以及 g 的调用位置等信息。
但正因为函数 g 是尾调用,所以函数 f 就结束了,所以当函数执行到函数 g 的时候,就可以删除函数 f(x) 的调用帧,只保留 g(3) 的调用帧。
这就是尾调用优化。 即只保留内层函数的调用帧。如果所有函数都是尾调用,那么就能实现每次执行都是只有一项调用帧,这将大大节省内存。
尾递归
尾递归其实也是尾调用的一种。函数调用自身,称为递归。
递归是非常消耗内存的,因为需要保存N个调用帧,所以说特别容易发生‘栈溢出’错误。但是尾递归可以完美解决。
function factorial(n){ if(n === 1) return 1; return n * factorial(n - 1);}factorial(5) // 120
这是一个计算5的阶乘,所以需要保存5个调用栈。
对了。突然想起来的直接说吧。
为了减少函数的耦合,我们应该使用arguments.callee
而如果改为尾递归的话,就只需要保留一个调用记录了。
function factorial(n, total = 1){ if(n === 1) return total; return arguments.callee(n - 1, n * total);}factorial(5) // 120
这样就可以实现尾递归优化,我们这里使用ES6的函数默认值写法来实现多参变一参,当然也可以使用 柯里化。
另外一个例子就是计算 Fibonacci 数列, 也能充分说明尾递归优化的重要性。
function Fibonacci (n) { if (n <= 1) return 1; return Fibonacci(n - 1) + Fibonacci(n - 2);}Fibonacci(10); // 89Fibonacci(100); // 堆栈溢出Fibonacci(500); // 堆栈溢出
而经过尾递归优化的 Fibonacci 数列实现如下:
function Fibonacci(n, ac1 = 1, ac2 = 1){ if(n <= 1) return ac2; return Fibonacci(n - 1, ac2, ac1 + ac2)}Fibonacci(100); // 573147844013817200000Fibonacci(1000); // 7.0330367711422765e+208
所以,‘尾调用优化’对于递归操作意义重大。在ES6中只要使用尾递归,就不会发生栈溢出,就会节省内存。
- 尾调用优化
- 尾调用优化
- 尾调用优化
- 尾调用优化
- 尾调用优化
- 谈一谈尾调用优化
- 尾调用优化
- 编译器的尾调用优化
- Erlang HIPE/x86尾调用优化
- lua程序性能优化(尾调用)
- 尾调用优化(Tail Call Optimization)
- JavaScript学习笔记_尾调用优化
- ES6-函数的扩展-尾调用优化
- MonoBehavior的调用优化
- ES6尾部调用优化
- 尾调用尾递归及其优化(笔记)
- ES6 尾调用优化--函数编程中规范
- .NET平台调用性能优化
- UUID生成方法总结
- PO、VO、BO、DTO、POJO、DAO之间的关系
- SQLite实用武器库(1)利用dump命令和read命令导出数据、导入数据
- PHP parse_ini_file() 函数 解析配置文件
- Shell脚本高级编程 二 正则表达式
- 尾调用优化
- 了解html页面的渲染过程
- 简单的检测输入的手机号码是否正确
- nginx平滑技术
- PL/SQL Develop中不使用TNS连接Oracle数据库
- Visual Studio 2015 + IIS Express 10.0 调试 ASP.NET 项目
- Linux chmod命令修改文件与文件夹权限命令代码
- kaa in Single node installation
- quartz调度器架构