Когда вы выполняете вызов (любой вызов), среда выполнения выделяет новый кадр стека и сохраняет параметры и локальные переменные вызываемой функции в новом кадре стека. Когда вы выполняете рекурсивный вызов, выделенные кадры содержат переменные с одинаковыми именами, но они хранятся в разных кадрах стека.
Чтобы продемонстрировать это, я буду использовать несколько упрощенную версию вашего примера:
let rec forLoop n =
if times > 0 then
printf "current %d" n
forLoop body (n - 1)
Теперь предположим, что мы вызываем forLoop 2
из какой-либо функции или модуля верхнего уровня программы. Среда выполнения выделяет стек для вызова и сохраняет значение параметра в кадре, представляющем вызов forLoop
:
+----------------------+
| forLoop with n = 2 |
+----------------------+
| program |
+----------------------+
Функция forLoop
печатает 2
и продолжает работать. Он выполняет рекурсивный вызов forLoop 1
, который выделяет новый кадр стека:
+----------------------+
| forLoop with n = 1 |
+----------------------+
| forLoop with n = 2 |
+----------------------+
| program |
+----------------------+
Поскольку 1 > 0
программа снова входит в ветку then
, печатает 1
и выполняет еще один рекурсивный вызов функции forLoop
:
+----------------------+
| forLoop with n = 0 |
+----------------------+
| forLoop with n = 1 |
+----------------------+
| forLoop with n = 2 |
+----------------------+
| program |
+----------------------+
На этом этапе функция forLoop
возвращается без выполнения каких-либо других вызовов, а стековые кадры удаляются один за другим, поскольку программы возвращаются после всех рекурсивных вызовов. Как видно из диаграмм, мы создали три разные переменные, которые были сохранены в разных кадрах стека (но все они были названы n
).
Стоит также отметить, что компилятор F # выполняет различные оптимизации, такие как tail-call , которые могут заменить вызов и выделение нового фрейма стека с использованием изменяемой переменной (которая более эффективная). Однако это всего лишь оптимизация, и вам не нужно об этом беспокоиться, если вы хотите понять ментальную модель рекурсии.