Как понять это продолжение?

(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")), который игнорирует свой аргумент и возвращает "привет"

Другие вопросы по тегам