Продолжением в данном случае является «вещь», которая получает возвращаемое значение вызова call/cc
. Таким образом:
(display (call/cc (lambda (k) (k 12)))
имеет тот же результат, что и
(display 12)
Продолжения в схеме «выглядят и чувствуются» как процедуры, но на самом деле они не ведут себя как процедуры. Одна вещь, которая может помочь вам лучше понять продолжения, — это преобразования CPS.
В преобразовании CPS вместо функции, возвращающей значение, она принимает параметр продолжения и вызывает продолжение с результатом. Таким образом, CPS-преобразованная функция sqrt
будет вызываться с (sqrt 64 k)
, и вместо того, чтобы возвращать 8, она просто вызывает (k 8)
в хвостовой позиции.
Поскольку продолжения (в CPS-преобразованной функции) вызываются в конце, функции не нужно беспокоиться о возвращении продолжения, и фактически в большинстве случаев они не должны возвращаться.
Имея это в виду, вот простой пример функции:
(define (hypot x y)
(sqrt (+ (* x x) (* y y))))
и его CPS-трансформированная версия:
(define (hypot x y k)
(* x x (lambda (x2)
(* y y (lambda (y2)
(+ x2 y2 (lambda (sum)
(sqrt sum k))))))))
(при условии, что *
, +
и sqrt
также были преобразованы CPS, чтобы принять аргумент продолжения).
Итак, самое интересное: CPS-преобразование call/cc
имеет следующее определение:
(define (call/cc fn k)
(fn k k))
С преобразованием CPS call/cc
легко понять и легко реализовать. Без CPS-преобразования call/cc
, скорее всего, потребует очень волшебной реализации (например, путем копирования стека и т. д.).
person
Chris Jester-Young
schedule
03.10.2012