コルーチンのお勉強

プログラミングGaucheのコルーチン(19.8)内容のお勉強。

P299に載っているexitのための手続きが定義されるほうのコルーチンを定義するためのマクロ定義の解読。

(use util.queue)
(define *tasks* (make-queue) )

(define-syntax define-coroutine
  (syntax-rules () 
    [ (_  (routine yield) body ...)
      (define (routine)
        (call/cc ( lambda(return) 
                   (define (yield)
                     (call/cc (lambda (cont)
                              (enqueue! *tasks* cont)
                              (return)) ) )
                   body ...))
        ((dequeue! *tasks*))) ] 
    [ (_ (routine yield exit) body ...)
      (define (routine)
        (call/cc ( lambda (escape) 
                   (call/cc ( lambda(return) 
                              (define (yield)
                                (call/cc (lambda (cont)
                                           (enqueue! *tasks* cont)
                                           (return)) ))
                   (define (exit) 
                     (call/cc (lambda (cont) 
                                ( (enqueue! *tasks* cont)
                                 (escape) ) ) ) )
                   body ...))
                   ((dequeue! *tasks*) ) ) ) )
     ]))

まず、exitのないほう。

コルーチン

これによって返される手続き(コルーチン)では、call/ccによりコルーチン呼び出し時の継続(return変数)の取得を行う。
このcall/cc内でbodyの実行を開始する(=他の継続が呼ばれないかぎり、このbodyの実行が続けられる)。call/ccを抜けて(<=他の継続が呼ばれて)このコルーチン呼び出し時の継続に戻ったときは、実行キューから継続を取り出し実行。

yield手続き

内側で定義される他のコルーチンを呼び出す手続き(yield)では、call/ccにより継続を取得して後で呼び出されるように実行キューに入れてから、
コルーチン呼び出し時の継続を呼び出す。この結果は実行キューからの取り出し+実行となる。

exitをつける場合

抜けるための継続取得

まず、再上位にcall/ccが呼び出される。これにより取得した継続がコルーチンの実行と実行キューの継続から抜けるためのものとなる。

exit手続き

exitの定義は、再開されたときに実行されるように現在の継続をキューに入れてから、コルーチンの最上位の継続を呼び出すことにより、コルーチンの実行と実行キューの継続から抜ける。