Это может показаться странным. Мы вызываем функцию из функции, но… это та же самая функция!
Может тут что-то не так? Вообще-то нет! Все дело в том, что сама по себе функция — это не ящик, это его описание. Когда вы вызываете функцию, тогда создается ящик, а после того, как функция выполнилась, ящик самоуничтожается. Поэтому когда вы вызываете ту же самую функцию из нее самой, просто создается еще один ящик.
Давайте это отследим: мы вызываем
factorial(3). 3 это не 0, поэтому первое условие игнорируется. Функция хочет произвести умножение чисел и вернуть ответ, но она не может — ей нужно знать второе число, для чего она вызывает
factorial(3-1) или
factorial(2).
Формируется новый идентичный ящик
factorial, он принимает число 2, это не 0, так что он пробует произвести умножение и вернуть ответ, но не может — ему нужно знать второе число, поэтому он вызывает
factorial(1).
Формируется новый идентичный ящик
factorial, он принимает число 1 и это снова не 0. Еще одна попытка произвести умножение и вернуть результат, происходит вызов
factorial(0) и этот ящик уже может мгновенно вернуть ответ — он возвращает 1.
1 возвращается в предыдущий ящик, умножается на 1 и ответ "1" возвращается в предыдущий ящик, умножается на 2 и ответ "2" возвращается в предыдущий ящик, умножается на 3 и ответ "6" возвращается во внешний мир и сохраняется в константе
answer.
Фуух!
Все это и есть рекурсия: что-то описывается через самого себя, содержит себя в своем описании. Когда дело касается математики или программирования, требуется два условия:
- Простой базовый случай или терминальный сценарий. Это точка, в которой нужно остановиться. В нашем примере это 0: мы остановили вычисление факториала когда в функцию был передан 0.
- Правило передвижения по рекурсии, углубление. В нашем случае это было n * factorial(n-1).
Еще один момент. Если проверить наш код с помощью линтера, то он выдаст ошибку
no-else-return. Последуем рекомендациями линтера и отрефакторим код: