Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
(define test-func
(if (call/cc (lambda (cc)
(begin
(set! *env* cc)
(> 3 5))
)) "true " "false "))
(display test-func)
(display (*env* #t))
(newline)
(display ...), которая вызывает test-func. Далее, test-func вызывает вычисление блока (if ...). Теперь, внимание, внутри условия if-a мы сохраняем текущее состояние с помощью (set! *env* cc). Т.е. теперь, если мы когда-нибудь вызовем (*env* value), то мы окажемся именно тут (****), «внутри» условия if-a. Но вместо вычисления этого условия будет подставлено значение value. Запомним это, двигаемся дальше.test-func спокойно завершит свою работу ((> 3 5) вернёт #f, if вернёт "false ", ну а test-func вернёт этот результат выше). display получит этот результат, распечатает его на экране и тоже завершит свою работу.(*env* #t). Мы переносимся в состояние ****! Всё идёт по-новой, точно так же, за тем лишь исключением, что условие if-a вычислено не будет, вместо него будет подставлено #t и поэтому в конце-концов будет распечатано "true ".(*env* #t)…(display (*env* #t))?(*env* #t). В этот момент мы, по сути, начинаем выполнять «другую» программу, сохранённую в *env*. Её «текст» выглядит так:(define test-func
(if (value) "true " "false "))
(display test-func)
(display (*env* #t))
(newline)
(*env* value)
value — это #t, то фактически исполняться будет всё вот так:(define test-func
(if (#t) "true " "false "))
(display test-func)
(display (*env* #t))
(newline)
"true ", она и будет распечтана. после этого интерпретатор пойдёт дальше по тексту, снова встретив (*env* #t)(define test-func
(if (value) "true " "false "))
(display test-func)
(sample-function)
(newline)
(define test-func
(if (call/cc (lambda (cc)
(begin
(set! *env* cc)
(> 3 5))
)) "true " "false "))
(display test-func)
(*env* #t)
(newline)
(define test-func
(if (#t) "true " "false "))
(display test-func)
(*env* #t)
(newline)
*env*), с чем работали, оказываясь в новой программе (часто пытаются описать это параллельной реальностью, прыжком во времени, и т.п.; я сознательно пострался избегать этого в статье, чтобы не создавать ложных ассоциаций, а попытаться донести, что же происходит на самом деле). Уничтожается стек вызова, подменяется тем стеком, который мы сохранили ранее. При этом с собой мы можем забрать один единственный кусок value, который «подменит» в «новой программе» часть кода, которая была обёрнута call/cc констуркцией.Можно поступить, например, так: если генератор не может выдать очередное значение, он возвращает значение-заглушку, например, «Stop Iteration».
Как это сделать? Проверьте себя на понимание, придумайте сами (добро пожаловать в комментарии). Нужно добавить всего одну строку кода внутри
(define (create-generator func) ...).
На всякий случай оставлю здесь решение, а то я как-то слишком долго над ним думал.
Нужно в конец этой лямбды добавить символ 'stop
(lambda ()
(call/cc
(lambda (cc)
(set! *return* cc)
(*env*))))(lambda ()
(call/cc
(lambda (cc)
(set! *return* cc)
(*env*)
'stop)))Тогда в случае, если yield не будет вызван, генератор вернёт символ 'stop.
Введение в продолжения и макросы на Scheme