Обновить

Как получить почти бесконечное зацикливание без использования циклов и без переполнения стека вызовов:

// Установите N = 64, и эта функция никогда не завершится  
// Количество вызовов (calls) = 2^(N+1)  
// Максимальная глубина вложенности = N

let calls = 0
const N = 18
function func(state, visited) {
  calls++
  if (calls > 10_000_000) {
    throw new Error('calls: ' + calls)
  }
  if (visited.includes(state)) return

  const newVisited = [...visited, state]

  func((state + 1) % N, newVisited)
  func((state + 1) % N, newVisited)
}

func(0, [])
console.log('calls:', calls)

Почему это работает без переполнения стека?

func(0, [])
├── func(1, [0])
│   ├── func(2, [0,1])
│   │   └── ... глубина растёт до N
│   │           и перебираются все возможные комбинации значений в newVisited
│   └── func(2, [0,1]) - возвращается, глубина УМЕНЬШАЕТСЯ
└── func(1, [0])       - второй вызов, стек уже освободился

А Garbage Collector (GC) при этом бесконечно удаляет созданные ранее массивы newVisited

Стек "дышит" - достигает максимума N, потом сворачивается, потом снова растёт. Это обход огромного дерева, имеющего небольшую глубину, но очень большую ширину. Это не бесконечная рекурсия. Но при N = 64 количество вызовов будет 2^65 (примерно 10^19) - это займёт тысячи лет, и стек никогда не переполнится.

Теги:
+1
Комментарии2

Публикации

Ближайшие события