В Алгоритме & Данные II мы использовали эти все времена, чтобы "выйти" или "возвратиться" из (долгой) функции
, например, BFS algorthm для пересечения деревьев с был реализован как это:
(define (BFS graph root-discovered node-discovered edge-discovered edge-bumped . nodes)
(define visited (make-vector (graph.order graph) #f))
(define q (queue.new))
(define exit ())
(define (BFS-tree node)
(if (node-discovered node)
(exit node))
(graph.map-edges
graph
node
(lambda (node2)
(cond ((not (vector-ref visited node2))
(when (edge-discovered node node2)
(vector-set! visited node2 #t)
(queue.enqueue! q node2)))
(else
(edge-bumped node node2)))))
(if (not (queue.empty? q))
(BFS-tree (queue.serve! q))))
(call-with-current-continuation
(lambda (my-future)
(set! exit my-future)
(cond ((null? nodes)
(graph.map-nodes
graph
(lambda (node)
(when (not (vector-ref visited node))
(vector-set! visited node #t)
(root-discovered node)
(BFS-tree node)))))
(else
(let loop-nodes
((node-list (car nodes)))
(vector-set! visited (car node-list) #t)
(root-discovered (car node-list))
(BFS-tree (car node-list))
(if (not (null? (cdr node-list)))
(loop-nodes (cdr node-list)))))))))
, Как Вы видите, алгоритм выйдет, когда обнаруженная узлом функция возвратит true:
(if (node-discovered node)
(exit node))
функция также даст "возвращаемое значение": 'узел'
, почему функция выходит, из-за этого оператора:
(call-with-current-continuation
(lambda (my-future)
(set! exit my-future)
, когда мы используем выход, он вернется к состоянию перед выполнением, освобождая стек вызовов и возвратит значение, которое Вы дали ему.
Так в основном, вызов-cc используется (здесь) для выпрыгивания из рекурсивной функции, вместо того, чтобы ожидать всей рекурсии для окончания отдельно (который может быть довольно дорогим при выполнении большой вычислительной работы)
другой меньший пример, делающий то же с вызовом-cc:
(define (connected? g node1 node2)
(define visited (make-vector (graph.order g) #f))
(define return ())
(define (connected-rec x y)
(if (eq? x y)
(return #t))
(vector-set! visited x #t)
(graph.map-edges g
x
(lambda (t)
(if (not (vector-ref visited t))
(connected-rec t y)))))
(call-with-current-continuation
(lambda (future)
(set! return future)
(connected-rec node1 node2)
(return #f))))
Продолжения могут использоваться для реализации исключений, отладчика.
Как насчет Google Mapplets API ? Существует набор функций (все окончание в Async
), которому Вы передаете обратный вызов. API-функция делает асинхронный запрос, добирается, это - результат, затем передает тот результат Вашему обратному вызову (как "следующая вещь сделать"). Много походит стиль передачи продолжения мне.
Этот пример шоу очень простой случай.
map.getZoomAsync(function(zoom) {
alert("Current zoom level is " + zoom); // this is the continuation
});
alert("This might happen before or after you see the zoom level message");
, Поскольку это - JavaScript нет никакого оптимизация последнего вызова , таким образом, стек вырастет с каждым вызовом в продолжение, и Вы в конечном счете возвратите поток управления к браузеру. Все равно я думаю, что это - хорошая абстракция.
Продолжения являются хорошей альтернативой потоку на запрос в программировании сервера (включая веб-приложение frontends.
В этой модели, вместо того, чтобы запустить новый (тяжелый) поток каждый раз, когда запрос входит, Вы только запускаете некоторую работу в функции. Затем когда Вы готовы заблокироваться на вводе-выводе (т.е. читающий из базы данных), Вы передаете продолжение в сетевой обработчик ответов. Когда ответ возвращается, Вы выполняете продолжение. С этой схемой можно обработать много запросов со всего несколькими потоками.
Это делает поток управления более сложным, чем использование блокирующихся потоков, но под большой нагрузкой, это более эффективно (по крайней мере, на сегодняшних аппаратных средствах).
Продолжения могут использоваться в "реальных" примерах каждый раз, когда процесс выполнения программы не линеен, или даже не предопределенный. Знакомая ситуация веб-приложения .
Продолжения используются некоторыми веб-серверами и веб-платформами, чтобы хранить информацию сессии. Объект продолжения создается для каждой сессии и затем используется каждым запросом в сессии.
Я создал свое собственное программное обеспечение поблочного тестирования. Прежде, чем выполнить тест, я храню продолжение прежде, чем выполнить тест, и затем при отказе, я (дополнительно) говорю интерпретатору схемы заскакивать в режим отладки и повторно вызывать продолжение. Таким образом, я могу ступить через проблематичный код действительно легко.
, Если Ваши продолжения являются сериализуемыми, можно также сохранить тогда на отказе приложения, и затем повторно вызвать их для получения подробной информации о значениях переменных, отслеживаниях стека, и т.д.
Побережье @Pat
Да, Побережье является ярким примером. Я просмотрел его код быстро и нашел это управление передачей иллюстрирования сообщения между компонентами способом на вид с сохранением информации в сети.
WAComponent >> call: aComponent
"Pass control from the receiver to aComponent. The receiver will be
temporarily replaced with aComponent. Code can return from here later
on by sending #answer: to aComponent."
^ AnswerContinuation currentDo: [ :cc |
self show: aComponent onAnswer: cc.
WARenderNotification raiseSignal ]
Настолько хороший!
Я приехал через реализацию amb
оператор в это сообщение от http://www.randomhacks.net , с помощью продолжений.
Вот то, что amb
делает оператор:
# amb will (appear to) choose values
# for x and y that prevent future
# trouble.
x = amb 1, 2, 3
y = amb 4, 5, 6
# Ooops! If x*y isn't 8, amb would
# get angry. You wouldn't like
# amb when it's angry.
amb if x*y != 8
# Sure enough, x is 2 and y is 4.
puts x, y
И вот реализация сообщения:
# A list of places we can "rewind" to
# if we encounter amb with no
# arguments.
$backtrack_points = []
# Rewind to our most recent backtrack
# point.
def backtrack
if $backtrack_points.empty?
raise "Can't backtrack"
else
$backtrack_points.pop.call
end
end
# Recursive implementation of the
# amb operator.
def amb *choices
# Fail if we have no arguments.
backtrack if choices.empty?
callcc {|cc|
# cc contains the "current
# continuation". When called,
# it will make the program
# rewind to the end of this block.
$backtrack_points.push cc
# Return our first argument.
return choices[0]
}
# We only get here if we backtrack
# using the stored value of cc,
# above. We call amb recursively
# with the arguments we didn't use.
amb *choices[1...choices.length]
end
# Backtracking beyond a call to cut
# is strictly forbidden.
def cut
$backtrack_points = []
end
мне нравится amb
!
Если вам нужно вызвать асинхронное действие и приостановить выполнение до получения результата, вы обычно либо опрашиваете результат, либо помещаете остальной код в обратный вызов, который будет выполнен асинхронным действием после завершения. С продолжениями вам не нужно делать неэффективный вариант опроса, и вам не нужно упаковывать весь ваш код, который будет выполняться после асинхронного события, в обратный вызов - вы просто передаете текущее состояние кода в качестве обратного вызова - и код фактически "просыпается", как только асинхронное действие завершается.
Оператор amb - хороший пример, который позволяет использовать пролог декларативное программирование.
Пока мы говорим, я кодирую отрывок из программного обеспечения для сочинения музыки на Scheme (я музыкант почти не разбираюсь в теории, лежащей в основе музыки, и я просто анализирую свои собственные произведения, чтобы увидеть, как математика стоит за этим. работает.)
Используя оператор amb, я могу просто указать ограничения, которым должна удовлетворять мелодия, и позволить Scheme вычислить результат.
Продолжения, вероятно, помещаются в Scheme из-за философии языка. Scheme - это структура, позволяющая вам понять любую парадигму программирования, найденную в другом языке, путем определения библиотек в самой Scheme. Продолжения предназначены для создания ваших собственных абстрактных структур управления, таких как return, break, или для включения декларативного программирования. Схема является более «обобщающей» и требует, чтобы такие конструкции могли быть определены и программистом.