Рекурсивная версия имеет следующую трассировку:
0: (FACTORIAL 10)
1: (FACTORIAL 9)
2: (FACTORIAL 8)
3: (FACTORIAL 7)
4: (FACTORIAL 6)
5: (FACTORIAL 5)
6: (FACTORIAL 4)
7: (FACTORIAL 3)
8: (FACTORIAL 2)
9: (FACTORIAL 1)
9: FACTORIAL returned 1
8: FACTORIAL returned 2
7: FACTORIAL returned 6
6: FACTORIAL returned 24
5: FACTORIAL returned 120
4: FACTORIAL returned 720
3: FACTORIAL returned 5040
2: FACTORIAL returned 40320
1: FACTORIAL returned 362880
0: FACTORIAL returned 3628800
Промежуточные результаты для рекурсивных вызовов используются для получения новых результатов. Должна быть некоторая память для хранения промежуточных результатов во время выполнения рекурсивных вызовов, чтобы объединить их и вычислить результат, когда они завершатся. Это хранилище находится в стеке вызовов, в кадрах стека .
«Итеративный» процесс имеет следующую трассировку:
0: (FACTORIAL 1 1 10)
1: (FACTORIAL 1 2 10)
2: (FACTORIAL 2 3 10)
3: (FACTORIAL 6 4 10)
4: (FACTORIAL 24 5 10)
5: (FACTORIAL 120 6 10)
6: (FACTORIAL 720 7 10)
7: (FACTORIAL 5040 8 10)
8: (FACTORIAL 40320 9 10)
9: (FACTORIAL 362880 10 10)
10: (FACTORIAL 3628800 11 10)
10: FACTORIAL returned 3628800
9: FACTORIAL returned 3628800
8: FACTORIAL returned 3628800
7: FACTORIAL returned 3628800
6: FACTORIAL returned 3628800
5: FACTORIAL returned 3628800
4: FACTORIAL returned 3628800
3: FACTORIAL returned 3628800
2: FACTORIAL returned 3628800
1: FACTORIAL returned 3628800
0: FACTORIAL returned 3628800
В случае итеративного процессаВы можете видеть, что результат всегда один и тот же: промежуточные результаты просто передаются обратно без изменений на верхний уровень. Другими словами, мы уже знаем окончательный результат, когда мы находимся на самом внутреннем вызове. Для любого промежуточного значения не требуется хранения, поскольку все хранится в аргументах функции. Вы также можете изменить аргументы с новыми значениями и циклом.
Фактически, это в основном то, что происходит, когда вызовы в хвостовой позиции оптимизируются: рекурсивные вызовы повторно используют тот же кадр стека, что и их вызывающий, выравнивая трассировку следующим образом:
(FACTORIAL 1 1 10)
(FACTORIAL 1 2 10)
(FACTORIAL 2 3 10)
(FACTORIAL 6 4 10)
(FACTORIAL 24 5 10)
(FACTORIAL 120 6 10)
(FACTORIAL 720 7 10)
(FACTORIAL 5040 8 10)
(FACTORIAL 40320 9 10)
(FACTORIAL 362880 10 10)
(FACTORIAL 3628800 11 10)
FACTORIAL returned 3628800
Выв конечном итоге поведение будет таким же, как если бы вы использовали конструкцию цикла. Но учтите, что даже с циклами вы можете воспользоваться этим трюком: устранение хвостовых вызовов не ограничивается рекурсивными вызовами, это может быть сделано всякий раз, когда вы можете безопасно повторно использовать фрейм при вызове функции.