- PVSM.RU - https://www.pvsm.ru -

Оптимизация хвостовой рекурсии в JavaScript

Привет, читатель.

Иногда для решении задачи приходится использовать Рекурсию [1], в которой есть свои плюсы и минусы. Я столкнулся с проблемой переполнения стека.

Максимальная глубина рекурсии ограничена движком JavaScript. Точно можно рассчитывать на 10000 вложенных вызовов, некоторые интерпретаторы допускают и больше, но для большинства из них 100000 вызовов – за пределами возможностей. Существуют автоматические оптимизации, помогающие избежать переполнения стека вызовов («оптимизация хвостовой рекурсии»), но они ещё не поддерживаются везде и работают только для простых случаев.

Пример рекурсивной функции:

function sum(n) {
  return n === 0 ? 0 : n + sum(n - 1)
}

sum(5) // 1 + 2 + 3 + 4 + 5  = 15
sum(100000) // Error: Maximum call stack size exceeded.


Хвостовая рекурсия [2] позволяет оптимизировать вызовы компилятором и уже есть в стандарте ES6 [3], но поддержка браузерами оставляет желать лучшего [4].

Пример хвостовой рекурсивной функции:

function sum(number, s = 0){
  return number === 0 ? s : sum(number - 1, s + number)
}

Но, без поддержки браузерами мы столкнемся с той же проблемой — переполнение стека. Можем попробовать использовать вместе с Trampolining [5].

Напишем декоратор:

function trampoline(fn) {
  return function(...args) {
    let result = fn.apply(fn.context, args)
    while (typeof result === 'function') {
      result = result()
    }

    return result
  }
}

Теперь наша рекурсивная функция должна возвращать функцию для нашего декоратора:

function sum(number, s = 0) {
  return number === 0 ? s : () => sum(number - 1, s + number)
}

Используем:

const trampolineSum = trampoline(sum)
trampolineSum(100000) // 5000050000

Это моя первая статья. Я старался не пересказывать понятия и указывал ссылку на источники. Тема не уникальная и давно существует на англоязычных сайтах, к сожалению на русском не нашел. Есть несколько реализаций данного способа оптимизации — это один, который я использовал.

Спасибо, за внимание. Хорошего дня :)

Автор: Михаил

Источник [6]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/328101

Ссылки в тексте:

[1] Рекурсию: https://learn.javascript.ru/recursion

[2] Хвостовая рекурсия: https://ru.wikipedia.org/wiki/Хвостовая_рекурсия

[3] ES6: http://www.ecma-international.org/ecma-262/6.0/#sec-tail-position-calls

[4] оставляет желать лучшего: https://kangax.github.io/compat-table/es6/#test-proper_tail_calls_%28tail_call_optimisation%29

[5] Trampolining: https://en.wikipedia.org/wiki/Tail_call#Through_trampolining

[6] Источник: https://habr.com/ru/post/464915/?utm_source=habrahabr&utm_medium=rss&utm_campaign=464915