Песочница

Рекурсия без роста продолжения

Выражайте циклы через именованный `let` или взаимно рекурсивные процедуры с вызовами в документированных хвостовых позициях.

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

LISPEX
(let loop ((xs (list 1 2 3 4)) (sum 0)) (if (null? xs) sum (loop (cdr xs) (+ sum (car xs)))))

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

OUTPUT
10

Разберите приём

  1. Найдите базовый случай. Когда xs пуст, готовый ответ уже хранится в sum.
  2. Уменьшайте остаток работы. (cdr xs) убирает ровно один элемент, поэтому любой конечный правильный список достигает базы.
  3. Переносите ответ вперёд. Новый аккумулятор вычисляется до loop, и рекурсивный вызов остаётся последним действием.

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

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

Проверьте себя

Добавьте 5 во входной список. Каков результат и остаётся ли рекурсивный вызов хвостовым?

Ответ

Результат — 15. Меняются только входные данные; loop по-прежнему является последним действием выбранной ветви.

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

Хвостовая безопасность не завершает бесконечный цикл и не отменяет бюджеты ресурсов.

Куда дальше

Для преобразования нехвостовой функции откройте главу о хвостовых вызовах, а для выбора fold — справочник процедур высшего порядка.

Правильные хвостовые вызовы · Процедуры высшего порядка