Jump to content

вызов с текущим продолжением

В Scheme программирования языке процедура call-with-current-continuation , сокращенно call/cc , используется в качестве оператора потока управления . Он был принят несколькими другими языками программирования.

Взяв функцию 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 к этому. Объект продолжения является значением первого класса и представлен как функция, применение функции единственной операцией которой является . Когда объект продолжения применяется к аргументу, существующее продолжение удаляется, а примененное продолжение восстанавливается на его месте, так что выполнение программы продолжается с той точки, в которой продолжение было захвачено, а аргумент продолжения затем становится «возвращаемое значение» вызова вызова/cc. Продолжения, созданные с помощью call/cc, могут вызываться более одного раза и даже из-за пределов динамического экстента приложения call/cc.

В информатике создание такого типа неявного состояния программы видимым как объект называется овеществлением . ( Схема не делает синтаксического различия между применением продолжений и функций.)

С помощью call/cc можно реализовать множество сложных операторов управления из других языков с помощью нескольких строк кода, например, McCarthy 's amb оператор для недетерминированного выбора , Prolog в стиле возврат , в стиле Simula 67 сопрограммы и их обобщения, Icon в стиле генераторы , или механизмы и потоки , или даже малоизвестный COMEFROM [ нужна ссылка ] .

Как показано в следующем примере, call/cc можно использовать для эмуляции функции оператора return, известной из языков C , которая отсутствует в Scheme :

(define (f return)                                                                                                                               
  (return 2)                                                                                                                                     
  3)

(f (lambda (x) x))
=> 3

(call-with-current-continuation f)
=> 2

Вызов f с аргументом обычной функции сначала применяет эту функцию к значению 2, а затем возвращает 3. Однако, когда f передается в call/cc (как в последней строке примера), применяется параметр (продолжение) к 2. заставляет выполнение программы перейти к точке вызова call/cc и заставляет call/cc возвращать значение 2. Затем оно выводится функцией отображения.

В следующем примере call/cc используется дважды: один раз для создания продолжения «возврата», как в первом примере, и один раз для приостановки итерации по списку элементов:

;; [LISTOF X] -> ( -> X u 'you-fell-off-the-end)
(define (generate-one-element-at-a-time lst)
  ;; Both internal functions are closures over lst

  ;; Internal variable/Function which passes the current element in a list
  ;; to its return argument (which is a continuation), or passes an end-of-list marker 
  ;; if no more elements are left. On each step the function name is 
  ;; rebound to a continuation which points back into the function body,
  ;; while return is rebound to whatever continuation the caller specifies.
  (define (control-state return)
    (for-each 
     (lambda (element)
               (set! return (call-with-current-continuation
                              (lambda (resume-here)
                                ;; Grab the current continuation
                               (set! control-state resume-here)
                               (return element))))) ;; (return element) evaluates to next return
     lst)
    (return 'you-fell-off-the-end))
  
  ;; (-> X u 'you-fell-off-the-end)
  ;; This is the actual generator, producing one item from a-list at a time.
  (define (generator)
    (call-with-current-continuation control-state))

  ;; Return the generator 
  generator)

(define generate-digit
  (generate-one-element-at-a-time '(0 1 2)))

(generate-digit) ;; 0
(generate-digit) ;; 1
(generate-digit) ;; 2
(generate-digit) ;; you-fell-off-the-end

Каждый раз, когда цикл собирается обработать другой элемент из списка, функция захватывает текущее продолжение и присваивает его переменной control-state. Эта переменная изначально является замыканием , которое проходит по всем элементам списка. По мере выполнения вычислений оно становится замыканием, которое перебирает суффикс данного списка. Хотя использование «call/cc» не является необходимым для линейной коллекции, такой как [LISTOF X], код обобщается на любую коллекцию, которую можно просмотреть.

Вызов с текущим продолжением может также выражать другие сложные примитивы. Например, следующий пример выполняет совместную многозадачность с использованием продолжений:

;; Cooperative multitasking using call-with-current-continuation
;; in 25 lines of scheme

;; The list of threads waiting to run. This is a list of one
;; argument non-returning functions (continuations, mostly)
;; A continuation is a non-returning function, just like (exit),
;; in that it never gives up control to whatever called it.

(define ready-list '())

;; A non-returning function. If there is any other thread
;; waiting to be run, it causes the next thread to run if there
;; is any left to run, otherwise it calls the original exit
;; which exits the whole environment.
(define exit
  ;; The original exit which we override.
  (let ((exit exit))
    ;; The overriding function.
    (lambda ()
      (if (not (null? ready-list))
	  ;; There is another thread waiting to be run.
	  ;; So we run it.
          (let ((cont (car ready-list)))
            (set! ready-list (cdr ready-list))
	    ;; Since the ready-list is only non-returning
	    ;; functions, this will not return.
            (cont #f))
	  ;; Nothing left to run.
	  ;; The original (exit) is a non returning function,
	  ;; so this is a non-returning function.
          (exit)))))

;; Takes a one argument function with a given
;; argument and forks it off. The forked function's new
;; thread will exit if/when the function ever exits.
(define (fork fn arg)
  (set! ready-list
        (append ready-list
		;; This function added to the 
		;; ready-list is non-returning,
		;; since exit is non returning.
		(list
		 (lambda (x)
		   (fn arg)
		   (exit))))))

;; Gives up control for the next thread waiting to be run.
;; Although it will eventually return, it gives up control
;; and will only regain it when the continuation is called.
(define (yield)
  (call-with-current-continuation
   ;; Capture the continuation representing THIS call to yield
   (lambda (cont)
     ;; Stick it on the ready list
     (set! ready-list
           (append ready-list
                   (list cont)))
     ;; Get the next thread, and start it running.
     (let ((cont (car ready-list)))
       (set! ready-list (cdr ready-list))
       ;; Run it.
       (cont #f)))))

В 1999 году Дэвид Мадор (изобретатель языка программирования Unlambda ) случайно обнаружил 12-символьный термин Unlambda, использующий call/cc, который печатал все натуральные числа последовательно в унарном представлении: ``r`ci`.*`ci. [1] Эта программа и очевидная тайна, окружающая ее эффект, привлекли некоторое внимание и широко известны как головоломка Инь-Ян . [2] Перевод схемы, предоставленный Мадором, выглядит следующим образом:

(let* ((yin
         ((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
       (yang
         ((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))))
    (yin yang))

Олег Киселев, автор реализации продолжения с разделителями для OCaml и разработчик интерфейса прикладного программирования (API) для манипулирования стеком с разделителями для реализации операторов управления, выступает за использование продолжений с разделителями вместо продолжений полного стека, которыми манипулирует вызов /cc: «Предложение call/cc в качестве основной функции управления, с точки зрения которой должны быть реализованы все остальные средства управления, оказывается плохой идеей. Производительность, утечки памяти и ресурсов, простота реализации, простота использования, простота рассуждения — все это аргументы против call/ куб.см.» [3]

Отношение к неконструктивной логике

[ редактировать ]

Соответствие Карри-Ховарда между доказательствами и программами связывает call/cc с законом Пирса , который расширяет интуиционистскую логику до неконструктивной классической логики : ((α → β) → α) → α. Здесь ((α → β) → α) — тип функции f , которая может либо возвращать значение типа α напрямую, либо применять аргумент к продолжению типа (α → β). Поскольку существующий контекст удаляется при применении продолжения, тип β никогда не используется и может считаться ⊥, пустым типом.

Принцип устранения двойного отрицания ((α → ⊥) → ⊥) → α можно сравнить с вариантом call-cc, который ожидает, что его аргумент f всегда будет оценивать текущее продолжение, обычно не возвращая значение. Вложения классической логики в интуиционистскую логику связаны с переводом в стиле передачи продолжения . [4]

Языки, реализующие вызов/cc

[ редактировать ]

См. также

[ редактировать ]
  1. ^ Дэвид Мадор, «ошеломляющий звонок / копия»
  2. ^ Инь Ван, «Понимание загадки Инь-Ян»
  3. ^ «Аргумент против вызова/cc» .
  4. ^ Соренсен, Мортен Хейне; Уржичин, Павел (2007). «Классическая логика и операторы управления». Лекции по изоморфизму Карри-Говарда (1-е изд.). Бостон, Массачусетс: Эльзевир. ISBN  978-0444520777 .
  5. ^ «Подпись CONT» . Стандартный ML штата Нью-Джерси . Bell Labs, Lucent Technologies. 28 октября 1997 г. Проверено 15 мая 2019 г.
  6. ^ «Класс: Продолжение» . Ruby-doc.org . Неурогами, Джеймс Бритт . Проверено 15 мая 2019 г.
  7. ^ Ковальке, Оливер (2014). «Переключение контекста с помощью вызова/cc» . Boost.org . Проверено 15 мая 2019 г.
  8. ^ «R: Вызов с текущим продолжением» .
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 1074971372a426b9d52f221301f70f70__1663488540
URL1:https://arc.ask3.ru/arc/aa/10/70/1074971372a426b9d52f221301f70f70.html
Заголовок, (Title) документа по адресу, URL1:
call-with-current-continuation - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)