Подтвердить что ты не робот

Что такое "подсказка продолжения"?

Я пытаюсь расшифровать документацию

call-with-continuation-prompt

Применяет proc к данному arg с текущим продолжением, расширенным подсказкой. Приглашение помечено тегом prompt-tag, который должен быть результатом либо default-continuation-prompt-tag по умолчанию (по умолчанию), либо make-continuation-prompt-tag. Результат proc - результат call-with-continuation-prompt.

Я понимаю часть, где говорится: "Применяет proc к данному arg с текущим продолжением", а затем он просто тарабарщится оттуда.

Что это значит для продолжения, чтобы быть "расширенным", и как "подсказка" делает это "расширение"?

4b9b3361

Ответ 1

Что такое подсказка, концептуально?

Схема вообще имеет идею продолжений, но Racket расширяет ее с идеей разграниченных продолжений. Идея продолжения состоит в том, что он фиксирует оставшиеся вычисления, которые нужно оценить. Я не буду пытаться объяснять продолжение вообще, так как это выходит за рамки этого вопроса.

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

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

Хорошо, но что это значит?

Концепция разграниченных продолжений на самом деле не совсем ясна, если не увидеть ее в действии по сравнению с неограниченными продолжениями.

Стандартные (без разделителей) продолжения

Рассмотрим следующий пример кода.

(define *k* #f)

(sqrt
 (+ 1 2 3
    (call/cc
     (λ (k)
       (set! *k* k)
       0))))

Этот код очень прост - он фиксирует продолжение и сохраняет его в глобальной привязке *k*. Само продолжение выглядит следующим образом:

(sqrt (+ 1 2 3 _))

(Где _ представляет "отверстие", которое должно быть заполнено при вызове продолжения.)

Применение этого продолжения будет работать точно так, как можно было бы ожидать.

> (*k* 3) ; evaluates (sqrt (+ 1 2 3 3))
3

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

Ограниченные продолжения

Что делать, если мы хотим захватить часть продолжения в *k*. Например, что, если мы хотим захватить это продолжение?

(+ 1 2 3 _) ; the inner portion of the last continuation

Мы можем сделать это, установив приглашение продолжения, которое отрегулирует, какая часть продолжения будет фактически записана.

(sqrt
 (call-with-continuation-prompt
  (λ ()
    (+ 1 2 3
       (call/cc
        (λ (k)
          (set! *k* k)
          0))))))

Теперь применение *k* дает внутренний результат:

> (*k* 3)
9

Аналогия для разграниченных продолжений

Продолжения могут быть несколько абстрактным понятием, поэтому, если приведенный выше пример кода не совсем ясен, рассмотрите эту аналогию.

Модель оценки представляет собой стек - каждый вызов функции выталкивает новый фрейм в стек, а возврат из функции выдает этот фрейм из стека. Мы можем визуализировать стек вызовов как стек карт.

Обычно, когда фиксируется продолжение, он фиксирует текущий кадр и все кадры под ним, как показано ниже.

Верхний уровень, представленный синим цветом, не фиксируется. Это эффективно приглашение по умолчанию в разделительной системе.

Однако при установке нового приглашения создается прозрачный разделитель между кадрами, который влияет на то, какие кадры захватываются как часть продолжения.

Этот разделитель ограничивает протяженность продолжения.

Приложение: Быстрые метки и барьеры продолжения

Это основы разграниченных продолжений, но есть и другие способы управления продолжениями, которые придают еще большую мощность системе продолжения (а также защиту от вредоносного кода), а также являются подсказками и барьерами продолжения.

Идея тега - это, по сути, "метка", которая тегирует данное приглашение. Используя аналогию с картой выше, каждому прозрачному делителю может быть присвоена метка. Затем, когда вы фиксируете продолжение, вы можете указать, чтобы он полностью возвращался к этой конкретной метке, даже если есть другие подсказки с другими метками между ними.

Барьеры продолжения, с другой стороны, являются мерой безопасности. Подобно подсказкам, они могут быть визуализированы как "разделители", сидящие между элементами стека вызовов, но вместо того, чтобы использоваться в качестве меток для контроля того, какая часть стека захвачена, они служат в качестве охранников, чтобы предотвратить продолжение прыжков "через" барьер.

Для получения дополнительной информации об этом, рассмотрите раздел в ссылке Racket на барьерах продолжения. Вот выдержка:

В частности, продолжение может быть заменено другим только тогда, когда замена не вводит никаких барьеров продолжения. Он может устранить барьеры продолжения только через переходы к продолжениям, которые являются хвостом текущего продолжения. Таким образом, барьер продолжения препятствует "прыжкам вниз" в продолжение, которое защищено барьером.