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

70 lines
4.8 KiB
Markdown
Raw Normal View History

2018-10-12 20:00:59 +00:00
---
title: TCO Tail Call Optimization
localeTitle: Оптимизация вызовов TCO
---
## Оптимизация звонков (TCO)
Tail Call Optimization ( **TCO** ) - решение проблемы переполнения стека при выполнении рекурсии.
### Проблема
Каждый вызов функции переносится в стек в памяти компьютера. Когда функция завершится, она выскочит из стека. В рекурсии функция вызывает себя, поэтому она продолжает добавлять в стек до тех пор, пока все эти функции не закончатся. Конечно, существует предел для этого стека. Когда имеется слишком много функций, слишком много вызовов добавляются в стек. Когда стек заполнен и вызывается функция, это приводит к **переполнению стека,** потому что стек уже заполнен. Рекурсивная функция не завершится и приведет к ошибке.
#### пример
Вот пример факториальной функции JavaScript с использованием рекурсии **без** TCO:
```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
```
Обратите внимание, что выполнение `fact` с аргументом 10000 приведет к **переполнению стека** .
### Использование TCO для решения проблемы
Чтобы решить эту проблему с помощью оптимизации Tail Call Optimization, оператор, вызывающий функцию, должен находиться в положении хвоста. Позиция хвоста - это последнее утверждение, которое должно выполняться в функции. Поэтому вызов функции для себя должен быть последним, что называется до завершения функции.
В предыдущем примере операция умножения выполняется последним в операторе `return x * fact(x-1)` , поэтому это была не окончательная операция функции. Таким образом, оптимизация хвоста не оптимизирована. Для того, чтобы он был оптимизирован для хвостового вызова, вам нужно сделать вызов для себя последней операцией функции.
#### пример
Ниже приведен пример факториальной функции JavaScript (ES5) с использованием рекурсии **с** TCO.
```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` на 10000 в этот раз **не приведет к переполнению стека** при апуске в браузере, который поддерживает ES6,_ потому что вызов `factTCO` является последней операцией функции.
### Почему это работает
Когда компилятор или интерпретатор замечают, что самозапуск является последней операцией функции, он выдает текущую функцию и выталкивает сам вызов в стек. Таким образом размер стека не изменяется. Таким образом, стек не переполняется из-за функции.
### Заметки
#### Дополнительная информация:
* [Что такое оплодотворение хвостового оперения?](https://stackoverflow.com/questions/310974/what-is-tail-call-optimization) (Переполнение стека)
* [Оптимизация звонков в ECMAScript 6](http://2ality.com/2015/06/tail-call-optimization.html) (2ality - блог доктора Акселя Раушмайера)