Существует также LISP-ощущение «батут», как описано в Википедии:
Используется в некоторых реализациях LISP,
батут это петля, которая итеративно
вызывает возвратные функции.
одного батута достаточно для
выразить все контрольные передачи
программа; программа так выражена
батут или в «батутном стиле»;
преобразование программы в батуте
стиль прыжки на батуте. Trampolined
функции могут быть использованы для реализации
вызовы хвостовой рекурсивной функции в
стековые языки
Допустим, мы используем Javascript и хотим написать простую функцию Фибоначчи в стиле продолжения передачи. Причина, по которой мы это сделаем, не имеет значения - например, перенести Scheme на JS или поиграть с CPS, который мы все равно должны использовать для вызова серверных функций.
Итак, первая попытка
function fibcps(n, c) {
if (n <= 1) {
c(n);
} else {
fibcps(n - 1, function (x) {
fibcps(n - 2, function (y) {
c(x + y)
})
});
}
}
Но выполнение этого с n = 25
в Firefox приводит к ошибке «Слишком много рекурсии!». Теперь это именно та проблема (отсутствует оптимизация хвостового вызова в Javascript), которую решает прыжок на батуте. Вместо того, чтобы (рекурсивно) вызывать функцию, давайте return
инструкция (thunk) для вызова этой функции, которая будет интерпретироваться в цикле.
function fibt(n, c) {
function trampoline(x) {
while (x && x.func) {
x = x.func.apply(null, x.args);
}
}
function fibtramp(n, c) {
if (n <= 1) {
return {func: c, args: [n]};
} else {
return {
func: fibtramp,
args: [n - 1,
function (x) {
return {
func: fibtramp,
args: [n - 2, function (y) {
return {func: c, args: [x + y]}
}]
}
}
]
}
}
}
trampoline({func: fibtramp, args: [n, c]});
}