Во-первых, вы должны понимать, что такое хвост.
Хвост - вызов, который не потребляет стек. Теперь вам нужно узнать, когда вы потребляете стек.
Давайте возьмем факторную пример:
(defun factorial (n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
Вот не хвост рекурсивная реализация факториала. Почему? Это связано с тем, что в дополнение к возврату из факториала существует ожидающий вычисления.
(* n ..)
Таким образом, вы укладываете n каждый раз, когда вы вызываете факториал. Теперь давайте напишем хвост рекурсивной факториала:
(defun factorial-opt (n &key (result 1))
(if (= n 1)
result
(factorial-opt (- n 1) :result (* result n))))
Здесь, результат передается в качестве аргумента функции. Итак, вы также потребляете стек, но разница в том, что размер стека остается постоянным. Таким образом, компилятор может оптимизировать его, используя только регистры и оставляя стек пустым.
factorial-opt
затем быстрее, но менее читаем. factorial
ограничен размером стека factorial-opt
нет. Итак, вы должны научиться распознавать функцию хвоста, чтобы знать, ограничена ли рекурсия.
Может быть какая-то техника компилятора для преобразования нерекурсивной функции без хвоста в хвостовую рекурсивную. Может быть, кто-то может указать здесь какую-то ссылку.
Наслаждайтесь рекурсией, как наслаждаться молотками. Иногда это лучший инструмент, но если вы хотите вставить винт, потяните за отвертку. – Xach
Мне нравится идея рекурсии. Меня это завораживает. Тем не менее, я бы никогда не использовал его ни в чем коммерческом. Конечно, он упрощает работу с кодом, но за счет эффективности и потребления памяти. Я предпочитаю итеративные версии, чтобы быть в безопасности. –