[plt-scheme] On continuations...

From: Daniel Pinto de Mello e Silva (daniel.silva at gmail.com)
Date: Sat Dec 18 14:52:53 EST 2004

On Fri, 17 Dec 2004 17:48:05 -0500, Matthias Felleisen
<matthias at ccs.neu.edu> wrote:
> 
> John, I think michael wants macros so that
> 
> (def part (n)
>       (if (= n 0)
>           (yield empty)
>           (return #f))
>       (for-each-yield part (- n 1)
>                       (lambda (p)
>                         (yield (cons 1 p))
>                         (if (and (pair? p) (or (null? (cdr p)) (< (car
> p) (cadr p))))
>                             (yield (cons (+ 1 (car sp)) (cdr sp)))))))
> 
> works. Daniel? -- Matthias
> 
> P.S. The macros are left as an exercise to the reader. Hint: use
> syntax-case.

The Pythonic way would use vectors, so here we go:

(module generator mzscheme
  ;; Implemetation of Generators by Oleg
  ;; http://okmij.org/ftp/Scheme/enumerators-callcc.html#Generators

  ;;; PLTized Oleg's "generative" for easier reading.
  ;;; Also renamed "suspend" to "yield."
  
  (provide generative define-gen)
  
  ;; (yield -> sxp) -> promise
  (define (generative g)
    (delay (let/cc k-main
             (define (yield val)
               (let/cc k-reenter
                 (k-main (cons val
                               (delay (let/cc k-new-main
                                        (set! k-main k-new-main)
                                        (k-reenter #f)))))))
             (g yield)
             (k-main null))))

  (define-syntax (define-gen stx)
    (syntax-case stx ()
      [(_ (id args ...) bodies ...)
       (with-syntax ([yield (datum->syntax-object stx 'yield)])
         #'(define (id args ...)
             (generative (lambda (yield)
                           bodies ...))))]))
  
  )

(module iterator mzscheme
  (require (lib "class.ss")
           "generator.ss")
  (provide iterator% define-igen exn:stop-iteration? iterate)
  
  (define-struct (exn:stop-iteration exn) ())
  
  (define iterator%
    (class object%
      (init-field init-cursor)
      (define promise init-cursor)
      (define/public (next)
        (unless promise
          (raise (make-exn:stop-iteration "StopIteration"
                                          (current-continuation-marks))))
        (let ([res (with-handlers ([void (lambda (e)
                                           (set! promise #f)
                                           (raise e))])
                     (force promise))])
          (if (pair? res)
              (begin (set! promise (cdr res))
                     (car res))
              (begin (set! promise #f)
                     (next)))))
      
      (define/public (reset)
        (set! promise init-cursor))
      (super-new)))
  
  (define (iterate proc iterator)
    (let loop ()
      (with-handlers ([exn:stop-iteration? void])
        (proc (send iterator next))
        (loop))))
  
  (define-syntax (define-igen stx)
    (syntax-case stx ()
      [(_ (id args ...) bodies ...)
       (with-syntax ([yield (datum->syntax-object stx 'yield)])
         #'(define (id args ...)
             (make-object iterator%
               (generative (lambda (yield)
                             bodies ...)))))]))
  
  )

(module iterator-test mzscheme
  (require "iterator.ss"
           (lib "class.ss"))
  
  (define-igen (iter lst)
    (for-each yield
              lst))

  (define-igen (iter/errors lst)
    (for-each yield
              lst)
    (raise "oops"))

  (define i (iter '(1 2 3)))
  (for-each (lambda (result)
              (printf "~a~n" result))
            (list (= 1 (send i next))
                  (= 2 (send i next))
                  (= 3 (send i next))
                  (with-handlers ([exn:stop-iteration? (lambda (e) #t)])
                    (send i next)
                    #f)))
  
  (iterate display (iter '(1 2 3)))
  (newline)

  (define ie (iter/errors '(1 2 3)))
  (for-each (lambda (result)
              (printf "~a~n" (force result)))
            (list (delay (= 1 (send ie next)))
                  (delay (= 2 (send ie next)))
                  (delay (= 3 (send ie next)))
                  ;; BUG! why isn't this exn caught?
                  ;(delay
                  ;  (with-handlers ([exn:stop-iteration? (lambda (e) #t)])
                  ;    (send ie next)
                  ;    #f))
                  ))
  
  )

(module vector-utils mzscheme
  (provide (all-defined))
  (define (vector->bool v)
    (not (zero? (vector-length v))))
  
  (define (vector+ v w)
    (define size (+ (vector-length v)
                    (vector-length w)))
    (define newv (make-vector size))
    (let loop ([idx 0])
      (unless (= idx (vector-length v))
        (vector-set! newv idx (vector-ref v idx))
        (loop (add1 idx))))
    (let loop ([idx (vector-length v)])
      (unless (= idx size)
        (vector-set! newv idx (vector-ref w (- idx
                                               (vector-length v))))
        (loop (add1 idx))))
    newv)
  
  (define (vector-for-each proc v)
    (let loop ([idx 0])
      (unless (= idx (vector-length v))
        (proc (vector-ref v idx))
        (loop (add1 idx)))))
  
  (define (subvector v start)
    (define size (- (vector-length v) start))
    (define newv (make-vector size))
    (let loop ([idx 0])
      (unless (= idx size)
        (vector-set! newv idx
                     (vector-ref v (+ start idx)))))
    newv)
)

(module partitions mzscheme
  
  (require "iterator.ss"
           "vector-utils.ss"
           (lib "class.ss"))
  (provide partitions)
  
  ;def partitions(n):
  ;       # base case of recursion: zero is the sum of the
  ;empty list
  ;       if n == 0:
  ;               yield []
  ;               return
  ;
  ;       # modify partitions of n-1 to form partitions of n
  ;       for p in partitions(n-1):
  ;               yield [1] + p
  ;               if p and (len(p) < 2 or p[1] > p[0]):
  ;                       yield [p[0] + 1] + p[1:]
  
  (define-igen (partitions n)
     (let/ec return
       (when (= n 0)
         (yield (vector))
         (return (void)))
       (iterate (lambda (p)
                  (yield (vector+ (vector 1) p))
                  (when (and (vector->bool p)
                             (or (< (vector-length p) 2)
                                 (> (vector-ref p 1)
                                    (vector-ref p 0))))
                    (yield (vector+ (vector (+ (vector-ref p 0) 1))
                                    (subvector p 1)))))
                (partitions (- n 1)))))
  
  )


Daniel



Posted on the users mailing list.