Поиск примеров “реального” использования продолжений

21
задан Sébastien RoccaSerra 10 September 2008 в 09:27
поделиться

12 ответов

В Алгоритме & Данные 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))))
16
ответ дан sven 29 November 2019 в 06:42
поделиться

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

0
ответ дан jfs 29 November 2019 в 06:42
поделиться

Как насчет 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 нет никакого оптимизация последнего вызова , таким образом, стек вырастет с каждым вызовом в продолжение, и Вы в конечном счете возвратите поток управления к браузеру. Все равно я думаю, что это - хорошая абстракция.

1
ответ дан Tom Dunham 29 November 2019 в 06:42
поделиться

Продолжения являются хорошей альтернативой потоку на запрос в программировании сервера (включая веб-приложение frontends.

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

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

3
ответ дан Greg 29 November 2019 в 06:42
поделиться

Продолжения могут использоваться в "реальных" примерах каждый раз, когда процесс выполнения программы не линеен, или даже не предопределенный. Знакомая ситуация веб-приложения .

3
ответ дан A. Rex 29 November 2019 в 06:42
поделиться

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

существует статья об этом подходе здесь.

5
ответ дан Dave Webb 29 November 2019 в 06:42
поделиться

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

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

6
ответ дан Jonathan Arkell 29 November 2019 в 06:42
поделиться

Побережье @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 ]

Настолько хороший!

7
ответ дан Sébastien RoccaSerra 29 November 2019 в 06:42
поделиться

Побережье:

Википедии
9
ответ дан Pat 29 November 2019 в 06:42
поделиться

Я приехал через реализацию 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!

5
ответ дан Sébastien RoccaSerra 29 November 2019 в 06:42
поделиться

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

1
ответ дан 29 November 2019 в 06:42
поделиться

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

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

Используя оператор amb, я могу просто указать ограничения, которым должна удовлетворять мелодия, и позволить Scheme вычислить результат.

Продолжения, вероятно, помещаются в Scheme из-за философии языка. Scheme - это структура, позволяющая вам понять любую парадигму программирования, найденную в другом языке, путем определения библиотек в самой Scheme. Продолжения предназначены для создания ваших собственных абстрактных структур управления, таких как return, break, или для включения декларативного программирования. Схема является более «обобщающей» и требует, чтобы такие конструкции могли быть определены и программистом.

2
ответ дан 29 November 2019 в 06:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: