ログ

見る価値ありません

call/ccでジェネレータを実装

最初に特化したジェネレータを返す関数を実装し、それを一般化する

数値をインクリメントしつつ無限ループする関数を返す関数を書く

(define (make-counter)
  (lambda ()
    (let rec ((c 0))
      (rec (+ c 1)))))

これに途中で中断できるように call/cc を追加する

(define (make-counter)
  (lambda ()
    (call/cc (lambda (return)
      (let rec ((c 0))
        (return c)
        (rec (+ c 1)))))))

また再開できるように call/cc を追加し、継続を保存して値を返す

(define (make-counter)
  (let ((restart #f))
    (lambda ()
      (call/cc (lambda (return)
        (let rec ((c 0))
          (call/cc (lambda (restart-cont)
            (set! restart restart-cont)
            (return c)))
          (rec (+ c 1))))))))

2回目以降の呼び出しは再開用継続を呼び出すようにする

(define (make-counter)
  (let ((restart #f))
    (lambda ()
      (if restart
          (restart)
          (call/cc (lambda (return)
            (let rec ((c 0))
              (call/cc (lambda (restart-cont)
                (set! restart restart-cont)
                (return c)))
              (rec (+ c 1)))))))))

またreturn用継続を呼び出しごとに更新する

(define (make-counter)
  (let ((return #f ) (restart #f))
    (lambda ()
      (call/cc (lambda (return-cont)
        (set! return return-cont)
        (if restart
            (restart)
            (let rec ((c 0))
              (call/cc (lambda (restart-cont)
                (set! restart restart-cont)
                (return c)))
              (rec (+ c 1)))))))))

これで数値をインクリメントしつつ返すジェネレータを実装できたので、これを一般化する

ジェネレータ本体部分で再開用継続を保存しつつ値を返せればいいので、これをクロージャにして引数として本体に渡して完成

(define (make-generator proc)
  (let ((return #f) (restart #f))
    (lambda ()
      (call/cc (lambda (return-cont)
        (set! return return-cont)
        (if restart
            (restart)
            (proc (lambda (x)
              (call/cc (lambda (cc)
                (set! restart cc)
                (return x)))))))))))

使い方

(define counter
        (make-generator (lambda (yield)
          (let rec ((c 0))
            (yield c)
            (rec (+ c 1))))))

(counter) ;; => 0
(counter) ;; => 1
(counter) ;; => 2