Как понять это продолжение?
(let ([x (call/cc (lambda (k) k))])
(x (lambda (ignore) "hi"))) => "hi"
Как я могу написать выполнение шагов этого продолжения?
3 ответа
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))).
Всегда есть подразумеваемый вызов k в верхней части тела лямбда-выражения, переданного call/cc
,
Мы знаем что k
является.
Итак, мы знаем, что это должно быть так же, как (для простоты чтения давайте обратимся x
находится в положении аргумента в y
"S.)
((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"))
, который игнорирует свой аргумент и возвращает "привет"