Продолжение с разделителями - Delimited continuation
В языки программирования, а продолжение с разделителями, составное продолжение или же частичное продолжение, является "кусочком" продолжение Рамка это было овеществленный в функция. В отличие от обычных продолжений, разграниченные продолжения возвращаться значение, и, таким образом, может быть повторно использовано и составлен. Управляющие ограничители, основа ограниченных продолжений, были введены Маттиас Фелляйзен в 1988 г.[1] хотя ранние намеки на составные и разграниченные продолжения можно найти в Кэролайн Талкотт в Стэнфордской диссертации 1984 г., в статье Феллейзена и Фридмана о PARL 1987 г.,[2] и диссертация Феллейзена 1987 года.[3]
История
Разграниченные продолжения были впервые введены Фелляйзеном в 1988 году.[1] с оператором, называемым , впервые представленный в техническом отчете в 1987 году,[2] вместе с быстрой конструкцией . Оператор был разработан как обобщение операторов управления, которые были описаны в литературе, например вызов / cc из Схема, Я ПЛАВАЮ с Оператор J, Джон С. Рейнольдс ' побег оператор и другие. Впоследствии многие конкурирующие операторы управления с разделителями были изобретены сообществом исследователей языков программирования, таким как Подсказка и контроль,[4] сдвиг и перезагрузить,[5] чашка,[6] fcontrol, и другие.
Примеры
В исследовательской литературе предлагались различные операторы для ограниченных продолжений.[7]
Одно предложение[5] предлагает два оператора управления: сдвиг и перезагрузить. В перезагрузить оператор устанавливает предел для продолжения, в то время как сдвиг оператор захватывает или перерабатывает текущее продолжение до самого внутреннего включающего перезагрузить. Например, рассмотрим следующий фрагмент в Схема:
(* 2 (перезагрузить (+ 1 (сдвиг k (k 5)))))В перезагрузить ограничивает продолжение, которое сдвиг захватывает (названо k в этом примере). Когда этот фрагмент выполняется, использование сдвиг свяжет k к продолжению (+ 1 []) куда [] представляет собой часть вычисления, которая должна быть заполнена значением. Это продолжение напрямую соответствует коду, который окружает сдвиг вверх к перезагрузить. Поскольку тело сдвига (т. Е. (к 5)) немедленно вызывает продолжение, этот код эквивалентен следующему:
(* 2 (+ 1 5))В общем, эти операторы могут кодировать более интересное поведение, например, возвращая захваченное продолжение. k как значение или вызывая k многократно. В сдвиг оператор передает захваченное продолжение k к коду в своем теле, который может либо вызывать его, либо создавать в результате, либо полностью игнорировать. Какой бы результат сдвиг производит для самых сокровенных перезагрузить, отбрасывая продолжение между перезагрузить и сдвиг. Однако, если продолжение вызывается, оно эффективно переустанавливает продолжение после возврата к перезагрузить. Когда все вычисления внутри перезагрузить завершается, результат возвращается продолжением с разделителями.[8] Например, в этом Схема код:
(перезагрузить (* 2 (сдвиг k КОД)))в любое время КОД призывает (к Н), (* 2 Н) оценивается и возвращается.
Это эквивалентно следующему:
(позволять ((k (лямбда (Икс) (* 2 Икс)))) КОД)Кроме того, как только все вычисления в сдвиг завершается, продолжение отменяется, и выполнение возобновляется вне перезагрузить. Следовательно,
(перезагрузить (* 2 (сдвиг k (k (k 4)))))призывает (к 4) сначала (который возвращает 8), а затем (к 8) (что возвращает 16). На данный момент сдвиг выражение прекратилось, а остальная часть перезагрузить выражение отбрасывается. Следовательно, итоговый результат - 16.
Все, что происходит за пределами перезагрузить выражение скрыто, т.е. на него не влияет передача управления. Например, это возвращает 17:
(+ 1 (перезагрузить (* 2 (сдвиг k (k (k 4))))))Разграниченные продолжения были впервые описаны независимо Фелляйзеном. и другие.[9] и Джонсон.[10] С тех пор они использовались во многих областях, особенно при определении новых операторы управления; см. Queinnec[11] для опроса.
Давайте посмотрим на более сложный пример. Позволять ноль быть пустым списком:
(перезагрузить (начинать (сдвиг k (минусы 1 (k (пустота)))) ;; (1) ноль))Контекст, захваченный сдвиг является (begin [*] null), куда [*] это дыра, где kбудет введен параметр. Первый звонок k внутри сдвиг оценивает этот контекст с помощью (пустота) = # заменяя отверстие, поэтому значение (k (недействительно)) является (начало # = ноль. Тело сдвиг, а именно (минусы 1 ноль) = (1), становится общей стоимостью перезагрузить выражение в качестве окончательного результата.
Чтобы усложнить этот пример, добавьте строку:
(перезагрузить (начинать (сдвиг k (минусы 1 (k (пустота)))) (сдвиг k (минусы 2 (k (пустота)))) ноль))Если мы закомментируем первый сдвиг, мы уже знаем результат, это (2); так что мы можем также переписать выражение следующим образом:
(перезагрузить (начинать (сдвиг k (минусы 1 (k (пустота)))) (список 2)))Это довольно знакомо, и его можно переписать как (минусы 1 (список 2)), то есть, (список 1 2).
Мы можем определить урожай используя этот трюк:
(определить (yield x) (shift k (cons x (k (void)))))
и использовать его при построении списков:
(перезагрузить (начинать (урожай 1) (урожай 2) (урожай 3) ноль)) ;; (список 1 2 3)Если мы заменим минусы с минусы потока, мы можем создавать ленивые потоки:
(определять (ручей-урожай Икс) (сдвиг k (минусы потока Икс (k (пустота))))) (определять ленивый пример (перезагрузить (начинать (ручей-урожай 1) (ручей-урожай 2) (ручей-урожай 3) stream-null)))Мы можем обобщить это и преобразовать списки в поток одним махом:
(определять (список-> поток хз) (перезагрузить (начинать (для каждого ручей-урожай хз) stream-null)))В более сложном примере ниже продолжение можно безопасно обернуть в тело лямбды и использовать как таковое:
(определять (для каждого-> стрим-мейкер для каждого) (лямбда (коллекция) (перезагрузить (начинать (для каждого (лямбда (элемент) (сдвиг k (минусы потока элемент (k игнорируется)))) коллекция) stream-null))))Часть между перезагрузить и сдвиг включает функции управления, такие как лямбда и для каждого; это невозможно перефразировать, используя лямбды[Почему? ].
Продолжения с разделителями также полезны в лингвистика: видеть Продолжение в лингвистике для подробностей.
Рекомендации
- ^ а б Маттиас Фелляйзен (1988). «Теория и практика первоклассных подсказок». Принципы языков программирования: 180–190. Дои:10.1145/73560.73576. ISBN 0-89791-252-7.
- ^ а б Felleisen; Фридман; Дуба; Меррилл (1987). Помимо продолжений (Технический отчет). Университет Индианы. 87-216.
- ^ Маттиас Фелляйзен (1987). Вычисления преобразования лямбда-v-CS: синтаксическая теория управления и состояния в императивных языках программирования высшего порядка (PDF) (Тезис).
- ^ Ситарам, Дораи; Фелляйзен, Маттиас (1990). «Контрольные ограничители и их иерархии» (PDF). Лисп и символьные вычисления.
- ^ а б Оливье Данви; Анджей Филински (1990). «Абстрагирование управления». LISP и функциональное программирование: 151–160. Дои:10.1145/91556.91622. ISBN 0-89791-368-X.
- ^ Гюнтер; Реми; Рике (1995). «Обобщение исключений и контроля в языках, подобных ML». Функциональные языки программирования и компьютерная архитектура.
- ^ См., Например, операторов, предлагаемых
ракетка / контрольРакетка библиотека [1]; следующие примеры можно запустить в Racket, используя(требуется ракетка / контроль) - ^ Гасбихлер, Мартин; Спербер, Майкл (2002). «Последняя смена для вызова / cc: прямая реализация сдвига и сброса». CiteSeerX 10.1.1.11.3425. Цитировать журнал требует
| журнал =(помощь) - ^ Фелляйзен, Матиас; Friedman, Daniel P .; Дуба, Брюс; Маррилл, Джон (февраль 1987). "За гранью продолжений" (PDF). Технический отчет 216. Департамент компьютерных наук, Университет Индианы. Цитировать журнал требует
| журнал =(помощь) - ^ Джонсон, Грегори Ф. (июнь 1987 г.). «GL: денотационный стенд с продолжениями и частичными продолжениями». Proc. Симпозиум SIGPLAN '87 по устным и устным переводам. С. 218–225.
- ^ Кейннек, Кристиан (апрель 1994 г.). «Библиотека операторов управления высокого уровня». École Polytechnique и INRIA -Rocquencourt. CiteSeerX 10.1.1.29.4790. Цитировать журнал требует
| журнал =(помощь)
внешняя ссылка
- Учебник по составным продолжениям на SchemeWiki
- Разграниченные продолжения в операционных системах, Олег Киселев и Чунг-чи Шан
- Собственные разграниченные продолжения в (байт-код и машинный код) OCaml
- Shift / Reset для самых маленьких (на русском)
- Несколько хороших статей о продолжениях с разделителями и первоклассных макросах