Какое текущее продолжение в следующем выражении?

В выражении (call/cc (lambda (k) (k 12))) есть три продолжения: (k 12), (lambda (k) (k 12)) и (call/cc (lambda (k) (k 12))). Какой из них является "текущим продолжением"?

А продолжения в некоторых книгах рассматриваются как процедура, ожидающая значения и возвращающаяся сразу после применения к значению. Это правильно?

Может ли кто-нибудь подробно объяснить, что представляют собой текущие продолжения?


person user1657564    schedule 03.10.2012    source источник


Ответы (2)


Такие вещи, как (k 12), не являются продолжениями. С каждым подвыражением в большей программе связано продолжение. Так, например, продолжением x в (* 3 (+ x 42)) является (lambda (_) (* 3 (+ _ 42))).

В вашем примере «текущим продолжением» (call/cc (lambda (k) (k 12))) будет все, что окружает это выражение. Если вы только что ввели его в приглашение схемы, вокруг него ничего нет, поэтому «текущее продолжение» — это просто (lambda (_) _). Если вы набрали что-то вроде (* 3 (+ (call/cc (lambda (k) (k 12))) 42)), то продолжение будет (lambda (_) (* 3 (+ _ 42))).

Обратите внимание, что лямбда-выражения, которые я использовал для представления «текущего продолжения», не совпадают с тем, что передается call/cc (названным k в вашем примере). k имеет особый контрольный эффект прерывания оставшейся части вычисления после оценки текущего продолжения.

person hzap    schedule 03.10.2012

Продолжением в данном случае является «вещь», которая получает возвращаемое значение вызова 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