Песочница

Правильные хвостовые вызовы и рекурсия

Хвостовое применение заменяет текущий кадр явного трамплина, поэтому собственная и взаимная хвостовая рекурсия не увеличивает логическое продолжение.

Когда это нужно

Хвостовые вызовы важны, когда цикл естественно записывается рекурсией. Если рекурсивный вызов — последнее действие, Лиспекс заменяет текущее продолжение вместо накопления нового.

Проверьте запуском

LISPEX
(let loop ((n 10000) (acc 0)) (if (= n 0) acc (loop (- n 1) (+ acc 1))))

Наблюдаемый результат

OUTPUT
10000

Разбор примера

Именованный let создаёт loop. Каждая ветвь либо возвращает acc, либо сразу вызывает loop с меньшим n и новым acc; после вызова не остаётся сложения. Поэтому десять тысяч шагов работают как один логический цикл.

Как рассуждать

  • Хвостовые позиции включают последние тела, обе ветви if, нормализованные cond/case, последние and/or, do, call-with-values и apply.
  • Правильный хвостовой вызов относится к росту продолжения, а не к одинаковому времени или неограниченному выполнению.
  • Даже для хвостовой рекурсии действуют детерминированные профильные пределы ресурсов.

Быстрый выбор

ФормаХвостовой вызов?Причина
(if done? answer (loop next))дарезультат выбранной ветви возвращается прямо
(begin (display x) (loop next))давызов — последняя форма
(+ x (loop next))нетпосле рекурсии остаётся сложение
(map loop items)нет для функции обратного вызоваmap ещё обрабатывает результат
(apply loop args) в хвостовой позициидаapply сохраняет хвостовую позицию

Частая ошибка

Нехвостовая рекурсия может достичь объявленного предела ресурсов.

Текущие границы

  • Чистая ресурсная ошибка не доказывает равенство порогов Native и WASM.

Куда дальше

Руководство по рекурсии показывает базовый случай и аккумулятор, а глава о ресурсах объясняет, почему хвостовая безопасность не означает безграничность.

Руководство по рекурсии · Детерминизм и ресурсы