Как этот итератор списка схемы использует call-with-current-continue?

Я пытаюсь прочитать этот код:

(define list-iter
  (lambda (a-list)
    (define iter
      (lambda ()
        (call-with-current-continuation control-state)))
    (define control-state
      (lambda (return)
        (for-each
          (lambda (element)
            (set! return (call-with-current-continuation
                           (lambda (resume-here)
                             (set! control-state resume-here)
                             (return element)))))
          a-list)
        (return 'list-ended)))
    iter))

Может ли кто-нибудь объяснить, как call-with-current-continuation работает в этом примере?

Спасибо


person Tyler Durden    schedule 12.04.2011    source источник
comment
Дубликат, вероятно, миллиарда других вопросов о вызове / копировании по SO. Самый канонический и, видимо, полезный, который я смог найти: stackoverflow.com/questions/612761/what-is- call-cc.   -  person amalloy    schedule 12.04.2011
comment
Я решил, что этот конкретный пример, использующий шаблон, похожий на yield, достаточно полезен, чтобы стоять сам по себе. Хотя, возможно, его стоит переименовать.   -  person acfoltzer    schedule 12.04.2011


Ответы (3)


Суть call-with-concurrent-continuation, или для краткости call/cc, - это возможность захватывать контрольные точки или продолжения во время выполнения программы. Затем вы можете вернуться к этим контрольным точкам, применив их как функции.

Вот простой пример, в котором продолжение не используется:

> (call/cc (lambda (k) (+ 2 3)))
5

Если не использовать продолжение, трудно заметить разницу. Вот несколько из них:

> (call/cc (lambda (k) (+ 2 (k 3))))
3
> (+ 4 (call/cc (lambda (k) (+ 2 3))))
9
> (+ 4 (call/cc (lambda (k) (+ 2 (k 3)))))
7

Когда вызывается продолжение, поток управления возвращается туда, где продолжение было захвачено call/cc. Думайте о выражении call/cc как о дыре, которая заполняется тем, что передается в k.

list-iter - это существенно более сложное использование call/cc, и может быть трудно начать его использовать. Во-первых, вот пример использования:

> (define i (list-iter '(a b c)))
> (i)
a
> (i)
b
> (i)
c
> (i)
list-ended
> (i)
list-ended

Вот набросок того, что происходит:

  1. list-iter возвращает процедуру без аргументов i.
  2. Когда вызывается i, мы немедленно захватываем продолжение и передаем его control-state. Когда вызывается это продолжение, привязанное к return, мы немедленно вернемся к тому, кто вызвал i.
  3. Для каждого элемента в списке мы берем новое продолжение и перезаписываем определение control-state этим новым продолжением, что означает, что мы продолжим оттуда в следующий раз, когда наступит шаг 2.
  4. После настройки control-state для следующего прохождения, мы передаем текущий элемент списка обратно в return продолжение, в результате чего получается элемент списка.
  5. Когда i вызывается снова, повторите, начиная с шага 2, пока for-each не выполнит свою работу для всего списка.
  6. Вызовите продолжение return с помощью 'list-ended. Поскольку control-state не обновляется, он будет возвращать 'list-ended каждый раз при вызове i.

Как я уже сказал, это довольно сложное использование call/cc, но я надеюсь, что этого достаточно, чтобы пройти через этот пример. Для более мягкого ознакомления с продолжениями я бы рекомендовал выбрать Опытный планировщик.

person acfoltzer    schedule 12.04.2011

Обычно он принимает функцию f в качестве параметра и применяет f к текущему контексту / состоянию программы.

Из википедии:
(define (f return)
(return 2)
3)

(display (f (lambda (x) x))) ; displays 3

(display (call-with-current-continuation f)) ; displays 2

Таким образом, в основном, когда f вызывается без текущего-продолжения (cc), функция применяется к 2, а затем возвращает 3. При использовании текущего-продолжения параметр применяется к 2, что заставляет программу перейти к точке, где Было вызвано текущее продолжение и, следовательно, возвращается 2. Его можно использовать для генерации возврата или для приостановки потока выполнения.

Если вы знаете C, подумайте об этом так: в C вы можете использовать указатель на функцию. Также у вас есть механизм возврата. Предположим, что функция return принимает параметр того же типа, что и функция. Предположим, вы можете взять его адрес и сохранить этот адрес в переменной или передать его в качестве параметра и позволить функциям возвращаться за вас. Его можно использовать для имитации броска / ловли или в качестве механизма для сопрограмм.

person prelic    schedule 12.04.2011

По сути, это:

(define (consume)
  (write (call/cc control)))

(define (control ret)
   (set! ret (call/cc (lambda (resume)
                        (set! control resume)
                        (ret 1))))
   (set! ret (call/cc (lambda (resume)
                        (set! control resume)
                        (ret 2))))
   (set! ret (call/cc (lambda (resume)
                        (set! control resume)
                        (ret 3)))))

(consume)
(consume)
(consume)

Надеюсь, это легче понять.

person xin wang    schedule 27.06.2015