freeCodeCamp/guide/chinese/software-engineering/tco-tail-call-optimization/index.md

70 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
title: TCO Tail Call Optimization
localeTitle: TCO尾调用优化
---
## 尾调用优化TCO
尾调用优化( **TCO** )是递归时堆栈溢出问题的解决方案。
### 问题
每次调用函数都会被推送到计算机内存中的堆栈中。当函数完成时,它将从堆栈中弹出。在递归中,函数调用自身,以便继续添加到堆栈,直到所有这些函数完成。当然,这个堆栈有一个限制。当调用的函数太多时,会向堆栈添加太多的调用。当堆栈已满并且调用了函数时,这会导致**堆栈溢出,**因为堆栈已满。递归函数将无法完成并将导致错误。
#### 例
以下是使用**不带** TCO的递归的JavaScript阶乘函数的示例
```javascript
function fact(x) {
if (x <= 1) {
return 1;
} else {
return x * fact(x-1);
}
}
console.log(fact(10)); // 3628800
console.log(fact(10000)); // RangeError: Maximum call stack size exceeded
```
请注意使用参数10000运行`fact`将导致**堆栈溢出** 。
### 使用TCO解决问题
要使用Tail Call Optimization来解决这个问题函数调用自身的语句应该处于尾部位置。尾部位置是要在函数中执行的最后一个语句。因此函数对自身的调用应该是在函数结束之前调用的最后一件事。
在前面的示例中,乘法运算在`return x * fact(x-1)`语句中最后执行,因此它不是函数的最终操作。因此,它不是尾调用优化的。为了使尾部调用优化,您需要调用自身函数的最后一个操作。
#### 例
下面是使用递归**与** TCO一个JavaScriptES5阶乘函数的一个例子。
```javascript
function fact(n) {
return factTCO(n, 1);
}
function factTCO(x, acc) {
if (x <= 1) {
return acc;
} else {
return factTCO(x-1, x*acc);
}
}
console.log(fact(10)); // 3628800
console.log(fact(10000)); // Infinity - Number too large, but unlike the unoptimized factorial, this does not result in stack overflow.
```
请注意,运行`fact` _在支持ES6_因为调用_浏览器中运行_时在10000这个时间**不会导致堆栈溢出** `factTCO`是函数的最后一个操作。
### 为什么会这样
当编译器或解释器注意到自调用是函数的最后一个操作时,它会弹出当前函数并将自调用推送到堆栈。这样,堆栈的大小不会改变。因此,堆栈不会因功能而溢出。
### 笔记
#### 更多信息:
* [什么是尾部调用optmization](https://stackoverflow.com/questions/310974/what-is-tail-call-optimization) (堆栈溢出)
* [ECMAScript 6中的尾调优化](http://2ality.com/2015/06/tail-call-optimization.html) 2ality - Axel Rauschmayer博士的博客