При первом вызове fac-cps
, k
- это функция identity
.Форма letfn
немедленно создает новую функцию, которая принимает аргумент v
и захватывает k
(identity
) в замыкании.
Затем проверяется, равен ли n
ноль, чтобазовый / терминальный регистр для этой рекурсии.
В противном случае, есть еще много работы, которая повторяется при уменьшении n
, но теперь передается новая функция cont
, которая закрылась над k
.Это будет происходить снова и снова, пока n
не достигнет нуля, создавая цветущий лук из вложенных замыканий.Это называется стиль передачи продолжения , и это то, что означает cps
в fac-cps
.Сравните это с чисто рекурсивным подходом, в котором эти значения, которые были захвачены в замыканиях, вместо этого были бы в стеке вызовов.
Обратите внимание, что работает в cont
(во всех вложенныхдруг в друга) не делается до самого конца, когда n
достигает нуля.Когда k
вызывается в случае терминала, это функция, которая может оборачивать многие другие функции.Другими словами, это создание thunk для оценки в конце.Например, (fac-cps 3)
в конечном итоге оценит эту функцию:
(fn [v] ((fn [v] ((fn [v] (identity (* v 3))) (* v 2))) (* v 1)))
, но я не понимаю, что v равно
v
- аргументдо cont
, поэтому она не будет известна, пока не будет вызван cont
.
то, что recur расширяется до
recur
- это специальная форма, которая-входит в функцию "рекурсивно", но без использования стека.Вы можете заменить recur
на fac-cps
, но вы получите переполнение стека, если n
достаточно большой.