(let ([x (call/cc (lambda (k) k))])
(x (lambda (ignore) "hi"))) => "hi"
Как я могу написать шаги выполнения этого продолжения?как понять это продолжение?
(let ([x (call/cc (lambda (k) k))])
(x (lambda (ignore) "hi"))) => "hi"
Как я могу написать шаги выполнения этого продолжения?как понять это продолжение?
Оператор call/cc
используется для вызова данной процедуры с текущим продолжением (отсюда и название call-with-current-continuation
). Чтобы понять, как это работает, нам нужно знать, что такое продолжение.
В программе, в тот момент, что call/cc
выполняется, продолжение выглядит следующим образом:.
CONT = (let ([x HOLE])
(x (lambda (ignore) "hi")))
где HOLE
является заполнителем для значения для подключения Другими словами, продолжение является оставшееся вычисление. Вы можете вставить значение в продолжение, если хотите прогрессировать.
Теперь call/cc
фиксирует это продолжение и передает его на процедуру (lambda (k) k)
. Вы можете видеть, что эта процедура сразу же возвращает продолжение. Таким образом, программа сводится к:
(let ([x CONT])
(x (lambda (ignore) "hi")))
Применение продолжения захваченного call/cc
заменяет текущее вычисление с этим продолжением закупорено значением, которое вы даете ему. Таким образом, приложение (x (lambda (ignore) "hi"))
превращается в:
(let ([x (lambda (ignore) "hi")])
(x (lambda (ignore) "hi")))
, а остальные должны следовать из того, что вы уже знаете о лямбды и приложения.
В первой строке [x (call/cc (lambda (k) k))]
мы создаем новое продолжение, которое связано с параметром k
в принятом lambda
. То, что k
возвращается и, в свою очередь, связано с локальной переменной x
, поэтому x
является продолжением.
Во второй строке x
вызывается с одним аргументом lambda
; аргумент игнорируется, и результатом вызова (lambda (ignore) "hi")
является "hi"
, который, наконец, возвращается в результате продолжения. Это эквивалентно простому вызову:
(call/cc
(lambda (k)
(k "hi")))
Почему это выражение оценивается как «привет»?
(let ([x (call/cc (lambda (k) k))])
(x (lambda (ignore) "hi")))
Первый шаг должны решить, что k
выглядит следующим образом:
(define k
(lambda (value)
(let ((x value))
(x (lambda (ignore) "hi")))))
Мы сразу видим, что это так же, как
(define k
(lambda (x)
(x (lambda (ignore) "hi"))))
Но я не упомянул одну маленькой детали , Если k
имеет значение , он как бы вызывается в положении хвоста.
Так (f (k 3))
для всех продолжений k
построен call/cc
является же, как (k 3)
. Это всегда немного сложно.
Итак, давайте использовать lambda^
, чтобы означать, что функция, которую она вводит , должна вызываться так, как если бы она находилась в положении хвоста.
(define k
(lambda^ (x)
(x (lambda (ignore) "hi"))))
Теперь мы знаем, что k
, мы также должны знать, что возвращение из (call/cc (lambda (k) k))
действительно использует по умолчанию.
Это должно был быть написано правильно, как
(call/cc (lambda (k) (k k))).
Существует всегда подразумеваемой вызов к в верхней части тела лямбды-выражении, переданном call/cc
.
Мы знаем, что такое k
.
Итак, мы знаем, что это должно быть таким же, как, (для удобства чтения давайте превратить x
'S в позиции аргумента в y
-х гг.)
((lambda^ (x) (x (lambda (ignore) "hi")))
(lambda^ (y) (y (lambda (ignore) "hi"))))
Таким образом, мы оцениваем как позиции к функциям.
После того, как мы вызываем функцию в позиции функции, мы сделали, так как он возглавляет lambda^
Таким образом, мы должны знать, что
((lambda^ (x) (x (lambda (ignore) "hi")))
(lambda^ (y) (y (lambda (ignore) "hi"))))
имеет значение, заменив x
((lambda^ (y) (y (lambda (ignore) "hi")))
(lambda (ignore) "hi"))
и еще один шаг, заменяющий y
ведет к
((lambda (ignore) "hi") (lambda (ignore) "hi"))
, который игнорирует свой аргумент и возвращает «привет»