В схеме компьютерного программирования языка, подпрограмма или функция вызов с током-продолжением , сокращенно вызова / см , используются в качестве потока управления оператором. Он был принят несколькими другими языками программирования.
Использование функции в f
качестве единственного аргумента (call/cc f)
внутри выражения применяется к текущему продолжению выражения. Например, ((call/cc f) e2)
это эквивалентно применению f
к текущему продолжению выражения. Текущее продолжение задается заменой (call/cc f)
переменной, c
связанной с лямбда-абстракцией, поэтому текущим продолжением является (lambda (c) (c e2))
. Применение f
к нему функции дает окончательный результат (f (lambda (c) (c e2)))
.
В качестве дополнительного примера в выражении (e1 (call/cc f))
продолжением подвыражения (call/cc f)
является (lambda (c) (e1 c))
, поэтому все выражение эквивалентно (f (lambda (c) (e1 c)))
. Другими словами, он берет «снимок» текущего контекста управления или состояния управления программы как объект и применяется f
к нему. Объект продолжения - это первоклассное значение, представленное как функция, с приложением функции в качестве единственной операции. Когда объект продолжения применяется к аргументу, существующее продолжение удаляется, а примененное продолжение восстанавливается на своем месте, так что поток программы будет продолжен в точке, в которой продолжение было захвачено, и аргумент продолжения затем становится "возвращаемое значение" вызова call / cc. Продолжения, созданные с помощью call / cc, могут вызываться более одного раза и даже извне динамического экстента приложения call / cc.
В информатике создание такого типа неявного состояния программы видимым как объект называется реификацией . ( Схема не делает синтаксических различий между применением продолжений и функций.)
С вызовом / куб.смом множество сложных операторов управления может быть реализовано из других языков через несколько строк коды, например, McCarthy «s amb
оператор для недетерминированного выбора , Пролог -стиля возвраты , Simula 67-стиль сопрограмма и обобщение их, Icon -стиль генераторы , или движки и потоки, или даже неясный COMEFROM .
Примеры
Как показано в следующем примере, call / cc можно использовать для эмуляции функции оператора return, известного из языков C- стиля, который отсутствует в Scheme :
( определить ( f return ) ( return 2 ) 3 )( дисплей ( f ( лямбда ( x ) x ))) ; отображает 3( отображение ( вызов-с-продолжением-текущим f )) ; отображает 2
Вызов f с обычным аргументом функции сначала применяет эту функцию к значению 2, а затем возвращает 3. Однако, когда f передается в call / cc (как в последней строке примера), параметр (продолжение) применяется к 2 заставляет выполнение программы перейти к точке, где был вызван call / cc, и заставляет call / cc возвращать значение 2. Затем оно выводится функцией отображения.
В следующем примере call / cc используется дважды: один раз для создания продолжения "return", как в первом примере, и один раз, чтобы приостановить итерацию по списку элементов:
;; [LISTOF X] -> (-> X u 'you-fall-off-the-end) ( define ( generate-one-element-at-a-time lst ) ;; Обе внутренние функции закрываются над lst ;; Внутренняя переменная / функция, которая передает текущий элемент в список ;; в свой возвращаемый аргумент (который является продолжением) или передает маркер конца списка ;; если больше не осталось элементов. На каждом шаге имя функции ;; возврат к продолжению, которое указывает обратно в тело функции, ;; в то время как return - это возврат к любому продолжению, которое указывает вызывающий. ( define ( control-state return ) ( for-each ( lambda ( element ) ( set! return ( call-with-current-continue) ( lambda ( resume-here ) ;; Захватить текущее продолжение) ( set! control-state resume- здесь ) ( return element ))))) ;; (return element) оценивается как следующий return lst ) ( return 'you-fall-off-the-end )) ;; (-> X u 'ты-упал до конца) ;; Это фактический генератор, производящий по одному элементу из списка за раз. ( Определить ( генератор ) ( вызов-с-тока продолжения управления-состояния )) ;; Вернуть генератор генератор )( определить генерировать цифру ( генерировать один элемент за раз ' ( 0 1 2 )))( генерировать цифру ) ;; 0 ( сгенерировать цифру ) ;; 1 ( сгенерировать цифру ) ;; 2 ( сгенерировать цифру ) ;; ты упал с конца
Каждый раз, когда цикл собирается обработать другой элемент из списка, функция захватывает текущее продолжение и присваивает его переменной 'control-state'. Эта переменная изначально является замыканием, которое проходит по всем элементам списка. По мере того, как вычисление продолжается, оно становится закрытием, которое повторяется через суффикс данного списка. Хотя использование «call / cc» не обязательно для линейной коллекции, например [LISTOF X]
, код обобщается на любую коллекцию, по которой можно пройти.
Call-with-current-continue также может выражать другие сложные примитивы. Например, в следующем примере выполняется совместная многозадачность с использованием продолжений:
;; Совместная многозадачность с использованием call-with-current-continue ;; в 25 строках схемы;; Список потоков, ожидающих запуска. Это список из одного ;; функции без возврата аргументов (в основном продолжения) ;; Продолжение - это невозвратная функция, как и (exit), ;; в том, что он никогда не уступает контроль тому, что его называет.( определить готовый список ' ());; Невозвратная функция. Если есть еще какой-нибудь поток ;; ожидая запуска, он заставляет запускать следующий поток, если есть ;; остается запустить, в противном случае вызывается исходный exit ;; который выходит из всего окружения. ( define exit ;; Исходный выход, который мы переопределяем. ( let (( exit exit )) ;; Переопределяющая функция. ( lambda () ( if ( not ( null? ready-list )) ;; Есть другой поток, ожидающий be run. ;; Итак, мы запускаем его. ( let (( cont ( car ready-list ))) ( set! ready-list ( cdr ready-list )) ;; Так как готовый список является только невозвратным ;; функции, это не вернет. ( cont #f )) ;; Ничего не осталось для запуска. ;; Исходная функция (exit) - невозвратная функция, ;; поэтому это невозвратная функция. ( exit )))) );; Принимает функцию с одним аргументом с заданным ;; аргумент и разветвляет его. Разветвленная функция new ;; поток выйдет, если / когда функция когда-либо завершится. ( define ( fork fn arg ) ( set! ready-list ( append ready-list ;; Эта функция, добавленная в ;; список готовности), не возвращается, ;; так как exit не возвращается. ( list ( lambda ( x ) ( fn arg ) ( выход ))))));; Передает управление следующему потоку, ожидающему запуска. ;; Хотя в конечном итоге он вернется, он теряет контроль ;; и восстановит его только при вызове продолжения. ( define ( yield ) ( call-with-current-continue ;; Захватить продолжение, представляющее ЭТОТ вызов yield ( lambda ( cont ) ;; Поместить его в список готовых ( set! ready-list ( append ready-list ( list cont ))) ;; Получить следующий поток и запустить его. ( Let (( cont ( car ready-list ))) ( set! Ready-list ( cdr ready-list )) ;; Запустить его. ( Cont #f )))))
В 1999 году Дэвид Madore (изобретатель Unlambda языка программирования) случайно обнаружил 12-символьный Unlambda срок, используя вызова / куб.см, что распечатаны все натуральные числа последовательно в одноместной представлении: ``r`ci`.*`ci
. [1] Эта программа и очевидная тайна, окружающая ее эффект, привлекли некоторое внимание и широко известны как загадка инь-ян . [2] Перевод схемы, предоставленный Мадоре, выглядит следующим образом:
( let * (( yin (( lambda ( cc ) ( display # \ @ ) cc ) ( call-with-current-continue ( lambda ( c ) c )))) ( ян (( lambda ( cc ) ( display # \ * ) cc ) ( call-with-current-continue ( лямбда ( c ) c ))))) ( инь янь ))
Критика
Олег Киселев, автор реализации продолжения с разделителями для OCaml и разработчик интерфейса прикладного программирования (API) для управления стеком с разделителями для реализации управляющих операторов, выступает за использование продолжений с разделителями вместо продолжений полного стека, которыми манипулирует / cc: "Предложение call / cc в качестве основной функции управления, с точки зрения которой должны быть реализованы все другие средства управления, оказывается плохой идеей. Производительность, утечки памяти и ресурсов, простота реализации, простота использования, легкость рассуждений - все это возражает против вызова / cc. " [3]
Отношение к неконструктивной логике
Соответствие Карри – Ховарда между доказательствами и программами связывает call / cc с законом Пирса , который расширяет интуиционистскую логику до неконструктивной классической логики : ((α → β) → α) → α. Здесь ((α → β) → α) - это тип функции f , которая может либо возвращать значение типа α напрямую, либо применять аргумент к продолжению типа (α → β). Поскольку существующий контекст удаляется при применении продолжения, тип β никогда не используется и может быть принят как, пустой тип.
Принцип исключения двойного отрицания ((α → ⊥) → ⊥) → α сравним с вариантом call-cc, который ожидает, что его аргумент f всегда будет оценивать текущее продолжение без обычного возврата значения. Вложения классической логики в интуиционистскую логику связаны с переводом стиля передачи продолжения . [4]
Языки, реализующие call / cc
Смотрите также
- Перейти к
- Продолжение прохождения стиля
- Волокно (информатика)
Рекомендации
- ^ Дэвид Madore, "вызов / куб.см ум-boggler"
- ↑ Инь Ван, «Понимание загадки Инь-Ян»
- ^ "Аргумент против вызова / cc" .
- ^ Соренсен, Мортен Гейне; Уржичин, Павел (2007). «Классическая логика и операторы управления». Лекции об изоморфизме Карри-Ховарда (1-е изд.). Бостон, Массачусетс: Эльзевир. ISBN 0444520775.
- ^ «Подпись CONT» . Стандартный ML Нью-Джерси . Bell Labs, Lucent Technologies. 1997-10-28 . Проверено 15 мая 2019 .
- ^ «Класс: Продолжение» . Ruby-doc.org . Neurogami, Джеймс Бритт . Проверено 15 мая 2019 .
- ^ Ковальке, Оливер (2014). «Переключение контекста с вызовом / копией» . Boost.org . Проверено 15 мая 2019 .
- ^ https://stat.ethz.ch/R-manual/R-devel/library/base/html/callCC.html
Внешние ссылки
- Краткое введение в call-with-current-continuation
- Определение call-with-current-continuationв спецификации схемы
- Юмористическое объяснение вызова с текущим продолжением от Роба Уорнока в Usenet на comp.lang.lisp
- Совместная многозадачность в схеме с использованием Call-CC
Эта статья основана на материалах, взятых из Free On-line Dictionary of Computing до 1 ноября 2008 г. и включенных в соответствии с условиями «перелицензирования» GFDL версии 1.3 или новее.