Может ли кто-нибудь объяснить call/cc простыми словами?
Я изучаю языковую ракетку и пытаюсь понять, для чего на самом деле предназначен call/cc. Может ли кто-нибудь объяснить это простыми словами и привести пример или два? Спасибо.
3 ответов
если у вас есть выражение (+ (* 2 3) (/ 10 2))
система схемы не будет оценивать все одновременно, но по частям. Порядок не указан в схеме, но давайте представим, что он слева направо (я думаю, что ракетка всегда делает слева направо):
ты (* 2 3)
, продолжением этого было бы вычислить (/ 10 2)
, потом (+ result1 result2)
. Способ, которым система схемы может это сделать, - преобразовать ваш код в продолжение прохождения стиле перед казнью. Выражение вверху получается что-то вроде этого:
(lambda (k)
(*& 2
3
(lambda (result1)
(/& 10
2
(lambda (result2)
(+& result1 result2 k))))))
теперь, процедуры с &
в конце концов такие же, как в схеме, за исключением того, что он принимает продолжение в качестве последнего аргумента. Пример одного из них: (define (+& a b k) (k (+ a b)))
. Все остальные делаются точно так же и считаются примитивными.
если вы примените это и передают display
или values
он будет отображаться или оценивать до 11.
Однако, если вы использовали call/cc
можно переопределить. Представьте себе это вариант:
(call/cc (lambda (k) (+ (* 2 3) (/ 10 (if (zero? a) (k +inf.0) a))
в схеме вы получаете ошибку при делении на ноль, и если это произойдет, вы хотите отменить остальные вычисления и сказать, что результат бесконечен. Код выше делает это, и если a
равно нулю, это не будет суммировать результаты двух предыдущих вычислений.. Он фактически действует как Гото. Версия CPS этого кода будет выглядеть примерно так:
(lambda (k)
(*& 2
3
(lambda (result1)
(zero?& a
(lambda (azero?)
(if azero?
(k +inf.0) ; continuation used here
(/& 10
a
(lambda (result2)
(+& result1 result2 k))))))))) ; and here
так что call/cc
делать? Ну, это позволяет вам кодировать ваш обычный способ, а не CPS например, как компьютер делает фактические вычисления, но вы получаете лучшее из двух миров, получив продолжение, так что вы можете сделать то же самое, как если бы вы написали его в CPS.
теперь представьте себе этот блок кода:
(let* ((c 10)
(r (call/cc (lambda (exit) exit))))
(display "Hello\n")
(cond ((zero? c) 'done)
(else (set! c (- c 1))
(r r))))
здесь вы видите, что я возвращаю продолжение как r
. Продолжение-это остальные вычисления, начинающиеся с установки r
, тогда как display
... Это фактически отображает "hello\n" 11 раз с момента вызова продолжение в низ он делает то же самое снова и снова.
как eval
Я стараюсь держать это на абсолютном минимуме, и когда я это делаю, я обычно делаю это, чтобы получить аборт от текущих вычислений. Например.
(define (lstadd1 lst)
(call/cc (lambda (exit)
(let loop ((lst lst))
(cond ((pair? lst) (cons (add1 (car lst)) (loop (cdr lst))))
((null? lst) '())
(else (exit #f))))))) ; not proper
(lstadd1 '(1 2 3)) ; ==> (2 3 4)
(lstadd1 '(1 2 . 3)) ; ==> #f
для более примеров я люблю Мэтт Mights страница С большим количеством примеров использования продолжений.
не все реализации call/cc
точно такие же, но, надеюсь, этот ответ может применяться ко всем общим вариациям, включая ракетку с небольшими проблемами. Эта история на самом деле основана на c
строение из Unlambda.
метафора для вызова / cc
вы археолог супергероя на раскопках на старом месте Майя. Вы обнаруживаете изысканно построенный, прекрасно сохранившийся каменный дверной проем в элегантной арке. Но это просто дверной проем, лежащий на боку; рядом с ним нет никакой стены, и кажется, что он никогда не был частью стены. Ваш персонал получает от него энергию, поэтому вы транспортируете его в целости и сохранности в лабораторию для изучения.
4:17 вечера
после прогулки проходя под аркой, вы, к своему удивлению, замечаете, что держите в руке кубическую коробку, достаточно большую, чтобы поместить в нее книгу, с крышкой и единственной светящейся кнопкой. Вы не помните, как подобрали такую коробку, или видели ее раньше. Позвони своему помощнику и спроси, откуда коробка. Помощник не знает. Если опустить коробку, кнопка перестанет светиться. Вы поднимаете его снова, кнопка снова светится. Он светится только тогда, когда вы его держите; если помощник поднимает коробку, он это делает не светиться. В шутку вы кладете скрепку в коробку, закрываете крышку и нажимаете кнопку.
4:23 вечера
"нет-нет! А что случилось с коробкой?- говорит помощник.
размещение коробки #3 и примечание внутри коробки #2, затем нажмите кнопку на коробке #2.2:01 АМ
выходя из арки вы держите пустую коробку, которая была помечена #3 - use to remember
, и записку, написанную твоим почерком. Время намного позже, чем ожидалось 5: 31 вечера, поэтому вы не продолжаете с вашим предполагаемым планом взлома. Следуя вашим инструкциям, вы сами проходите через арку снова, получая новый ящик, который вы называете #4 - use to forget
. Вы звоните в полицию, как было указано в записке, не зная почему, и слышите в новостях, что международная шпионская сеть распалась в вашем родном городе.
этот текст класс Я использую в своих лекциях о продолжений. Первоначально он был основан на Плай, но значительно расширен с более практичным кодом, а также включает в себя некоторые примеры со страницы Мэтью май. (Короче говоря, многие люди внесли в это косвенный вклад. Она не очень короткая, но ее легко прочитать.
возможно, однажды это позволит опубликовать это в качестве ответа, но на данный момент нет способа поместить столько текста здесь.