;; Generators a-la Icon/Python/Ruby. ;; ;; Adapted from material from Chapter 19 of Shriram Krishnamurthi's ;; "Programming Languages: Application and Interpretation". ;; ;; Example usage: ;; ;; > (define-generator (odds-producer) ;; (let loop ((n 1)) ;; (yield n) ;; (loop (+ n 2)))) ;; > (define odds (odds-producer)) ;; > (odds) ;; 1 ;; > (odds) ;; 3 ;; > (+ (odds) (odds) (odds) (odds)) ;; 32 ;; ;; This seems to be a popular homework exercise. *grin* ;; ;; ;; See: ;; ;; http://list.cs.brown.edu/pipermail/plt-scheme/2004-December/007498.html ;; http://schemecookbook.org/Cookbook/IteratorGenerators ;; ;; for other, better implementations of the concept. I cooked this ;; one up just so I could make sure I understood continuations and ;; macros better. (module generators mzscheme (provide make-generator define-generator/with-fail-thunk-and-keyword define-generator) ;; Constructs a generator value out of a template function. ;; gen-template should be a function that takes the yielding function (define (make-generator gen-template fail-thunk) (let ((return #f) (resume #f)) (lambda () (let/cc r (set! return r) (if resume (resume return) (begin (gen-template (lambda (val) (let/cc res (set! resume res) (return val)))) ;; At the end of the generation, just set resumption ;; to send over the fail thunk. (let/cc res (set! resume res)) (return (fail-thunk)))))))) ;; Convenient syntax to define a generator: (define-syntax (define-generator/with-fail-thunk-and-keyword stx) (syntax-case stx () [(define-generator/with-fail-thunk fun-name (lambda args body ...) fail-thunk yield-keyword) (syntax/loc stx (define fun-name (lambda args (make-generator (lambda (yield-keyword) body ...) fail-thunk))))])) ; ;; example usage: ; (define-generator/with-fail-thunk-and-keyword integers-between-m-n ; (lambda (m n) ; (let loop ((i m)) ; (when (< i n) ; (send i) ; (loop (add1 i))))) ; (lambda () 'no-more-integers) ; send) ;; Defines a generator. Introduces keyword 'yield that escapes and returns values to the caller. (define-syntax (define-generator stx) (syntax-case stx () [(define-generator (fun-name args ...) body ...) (with-syntax [(yield (datum->syntax-object (syntax define-generator) 'yield))] (syntax/loc stx (define-generator/with-fail-thunk-and-keyword fun-name (lambda (args ...) body ...) (lambda () (error "no more values to yield")) yield )))] [(define-generator fun-name fun-val-expr) (with-syntax [(yield (datum->syntax-object (syntax define-generator) 'yield))] (syntax/loc stx (define-generator/with-fail-thunk-and-keyword fun-name fun-val-expr (lambda () (error "no more values to yield")) yield)))])) ; ; ;; Example: produces numbers starting from n onward. ; (define-generator (numbers n) ; (let loop ((i n)) ; (yield i) ; (loop (add1 i)))) ; )