Проверьте запуском
LISPEX
(let loop ((xs (list 1 2 3 4)) (sum 0)) (if (null? xs) sum (loop (cdr xs) (+ sum (car xs)))))Наблюдаемый результат
OUTPUT
10Разберите приём
- Найдите базовый случай. Когда
xsпуст, готовый ответ уже хранится вsum. - Уменьшайте остаток работы.
(cdr xs)убирает ровно один элемент, поэтому любой конечный правильный список достигает базы. - Переносите ответ вперёд. Новый аккумулятор вычисляется до
loop, и рекурсивный вызов остаётся последним действием.
Как рассуждать
- Передавайте аккумуляторы параметрами вместо работы после возврата рекурсии.
- Хвостовая позиция сохраняется в последних путях
if,begin,and/or,cond,case,do,call-with-valuesиapply. - В тестах ограничивайте вход и отличайте глубину continuation от стоимости выполнения.
Проверьте себя
Добавьте 5 во входной список. Каков результат и остаётся ли рекурсивный вызов хвостовым?
Ответ
Результат — 15. Меняются только входные данные; loop по-прежнему является последним действием выбранной ветви.
Частая ошибка
Хвостовая безопасность не завершает бесконечный цикл и не отменяет бюджеты ресурсов.
Куда дальше
Для преобразования нехвостовой функции откройте главу о хвостовых вызовах, а для выбора fold — справочник процедур высшего порядка.