[plt-scheme] generators with sequences

From: Robby Findler (robby at eecs.northwestern.edu)
Date: Thu Aug 27 19:30:37 EDT 2009

One other thought: did your explorations with the call/cc variant
suggest ways that Ruby might be broken? Did you try to confirm that?
It may shed some light on the Scheme version.

Robby


On Wed, Aug 26, 2009 at 9:46 AM, Matthias Felleisen<matthias at ccs.neu.edu> wrote:
>
> Write up a section for the Guide, check it in as an aux library for
> for-loops, and advertise. I am sure Matthew can show you where the Guide
> lives and what the editorial rules are. -- Matthias
>
>
>
>
> On Aug 26, 2009, at 1:29 AM, Jon Rafkind wrote:
>
>> I wrote a generator syntax for use with the v4 `for' sequences. Some
>> example usages are below but the main point is you can say (yield 5) when
>> you want a function to produce a value that can be used as a sequence. It
>> took me a few tries to get this right and I left most of my attempts in so
>> others can maybe learn what not to do. This could go into collects/, so if
>> anyone has any improvements please share! The last attempt (test4) is my
>> best effort so far.
>>
>> #lang scheme
>>
>> (require scheme/control)
>> (require scheme/stxparam)
>> (define (check v1 v2)
>> (when (not (equal? v1 v2))
>>  (printf "test failed expected ~a actual ~a\n" v1 v2)))
>>
>> (define (do-tests for-function)
>> (check (for/list ([i (in-range 0 10)])
>>                 i)
>>       (for/list ([i (in-range 0 10)])
>>                 i))
>>
>> #;
>> (let ([x 0])
>>  (for/list ([i (for-function)])
>>            (printf "i ~a x ~a\n" i x)
>>            (set! x (add1 x))
>>            i))
>>
>> (check (for/list ([i (in-range 0 10)])
>>                 i)
>>       (let ([x 0])
>>         (for/list ([i (for-function)])
>>                   ;; (printf "i ~a x ~a\n" i x)
>>                   (set! x (add1 x))
>>                   i)))
>>
>> (for/list ([i (for-function)]
>>           [j (in-range 0 5)])
>>          ;; (printf "i ~a j ~a\n" i j)
>>          (list i j))
>>
>> (check (for/list ([i (in-range 0 10)]
>>                  [j (in-range 0 5)])
>>                 (list i j))
>>       (for/list ([i (for-function)]
>>                  [j (in-range 0 5)])
>>                 ;; (printf "i ~a j ~a\n" i j)
>>            (list i j)))
>>
>> (check (for/list ([i (in-range 0 10)]
>>                  [j (in-range 0 10)])
>>                 (list i j))
>>       (for/list ([i (for-function)]
>>                  [j (for-function)])
>>                 (list i j)))
>>
>> (check (for/list ([(v vi) (in-indexed (in-range 0 10))])
>>                 (list v vi))
>>       (for/list ([(v vi) (in-indexed (for-function))])
>>                 (list v vi)))
>>
>> (check (for/list ([v (in-sequences (in-range 0 10) (in-range 0 10))])
>>                 v)
>>       (for/list ([v (in-sequences (for-function) (for-function))])
>>                 v))
>>
>> ;; doesn't work yet
>> #;
>> (check (for/list ([v (in-cycle (in-range 0 10))]
>>                  [x (in-range 0 100)])
>>                 v)
>>       (for/list ([v (in-cycle (for-function))]
>>                  [x (in-range 0 100)])
>>                 (printf "~a ~a\n" v x)
>>                 v))
>> )
>>
>> ;; completely broken version using call/cc and creating too many sequence
>> objects
>> (define test1
>> (let ()
>>  (define-syntax yield
>>    (syntax-rules ()
>>      [(_ yielder value)
>>       (call/cc (lambda (ret)
>>                  (let ([s (make-do-sequence (lambda ()
>>                                               (values
>>                                                 (lambda (i) value)
>>                                                 (lambda (x) (ret #f))
>>                                                 0
>>                                                 (lambda (x) #t)
>>                                                 (lambda (v) (eq? v value))
>>                                                 (lambda (x v) #t))))])
>>                    (yielder s))))]))
>>
>>  (define-syntax define-generator
>>    (syntax-rules ()
>>      [(_ ret (name args ...) body0 bodies ...)
>>       (define (name args ...)
>>         (call/cc (lambda (ret)
>>                    body0 bodies ...
>>                    (make-do-sequence (lambda ()
>>                                        (values
>>                                          (lambda (i) (void))
>>                                          (lambda (x) (void))
>>                                          0
>>                                          (lambda (x) #f)
>>                                          (lambda (x) #f)
>>                                          (lambda (x y) #f))))
>>                    )))]))
>>
>>
>>  (define-generator yielder (blah)
>>                    (for ([x (in-range 0 10)])
>>                         (yield yielder x)))
>>
>>  (printf "test1\n")
>>  (do-tests blah)
>>  ))
>>
>> ;; working version using prompt/control
>> (define test2
>> (let ()
>>  (define-syntax yield
>>    (syntax-rules ()
>>      [(_ value)
>>       ;; Capture the current delimited continuation, f, and pass back to
>> the last prompt a,
>>       ;; value and a function that installs a new prompt and continues
>> where f left off.
>>       (control f (values value (lambda () (prompt (f)))))]))
>>
>>  (define-syntax lambda-generator
>>    (syntax-rules ()
>>      [(_ (args ...) body0 bodies ...)
>>       (lambda (args ...)
>>         (let* ([current (lambda ()
>>                           (prompt
>>                             body0 bodies ...
>>                             ;; (printf "now what\n")
>>                             (values #f #f)
>>                             ))]
>>                  [pos 0]
>>                  [seq (make-do-sequence (lambda ()
>>                                         (values
>>                                           (lambda (i) (let-values ([(value
>> next) (current)])
>>                                                         ;; (printf "Next
>> value is ~a\n" value)
>>                                                         (set! current
>> next)
>>                                                         value))
>>                                           (lambda (x) (add1 x))
>>                                           0
>>                                           (lambda (x) current)
>>                                           (lambda (v) current)
>>                                           (lambda (x v) current))))])
>>           seq))]))
>>
>>  (define-syntax define-generator
>>    (syntax-rules ()
>>      [(_ (name args ...) body0 bodies ...)
>>       (define name (lambda-generator (args ...) body0 bodies ...))]))
>>
>>  (define-generator (blah)
>>                    (for ([x (in-range 0 10)])
>>                         (yield x)))
>>
>>  (printf "test2\n")
>>  (do-tests blah)
>>
>>  #;
>>  (printf "test3\n")
>>  #;
>>  (do-tests (lambda ()
>>              (make-do-sequence (lambda ()
>>                                  (values
>>                                    (lambda (i) i)
>>                                    (lambda (x) (add1 x))
>>                                    0
>>                                    (lambda (x) (< x 10))
>>                                    (lambda (v) #t)
>>                                    (lambda (x v) #t))))))
>>
>>  ;; a cute example
>>  #;
>>  (let ()
>>    ;; generate infinte list of unique names
>>    (define-generator (machine-names)
>>                      (define letters "abcdefghijklmnopqrstuvwxyz")
>>                      (let loop ([len 0])
>>                        ;; generates strings of length num
>>                        (define-generator (generate num)
>>                                          (for ([letter (in-string
>> letters)])
>>                                               (if (= num 0)
>>                                                 (yield (string letter))
>>                                                 (for ([l2 (generate (- num
>> 1))])
>>                                                      (yield (string-append
>> (string letter) l2))))))
>>                        (for ([str (generate len)])
>>                             (yield str))
>>                        (loop (add1 len))))
>>    (for ([s (machine-names)]
>>          [j (in-range 0 1000)])
>>         (printf "~a: ~a\n" j s)))
>>
>>  ))
>>
>> ;; better version using shift/reset
>> (define test3
>> (let ()
>>  (define-syntax yield
>>    (syntax-rules ()
>>      [(_ value)
>>       (shift f (values value f))]))
>>
>>  (define-syntax lambda-generator
>>    (syntax-rules ()
>>      [(_ (args ...) body0 bodies ...)
>>       (lambda (args ...)
>>         (let* ([current (lambda ()
>>                           (reset
>>                             body0 bodies ...
>>                             ;; (printf "now what\n")
>>                             (values #f #f)
>>                             ))]
>>                  [pos 0]
>>                  [seq (make-do-sequence (lambda ()
>>                                         (values
>>                                           (lambda (i) (let-values ([(value
>> next) (current)])
>>                                                         ;; (printf "Next
>> value is ~a\n" value)
>>                                                         (set! current
>> next)
>>                                                         value))
>>                                           (lambda (x) (add1 x))
>>                                           0
>>                                           (lambda (x) current)
>>                                           (lambda (v) current)
>>                                           (lambda (x v) current))))])
>>           seq))]))
>>
>>  (define-syntax define-generator
>>    (syntax-rules ()
>>      [(_ (name args ...) body0 bodies ...)
>>       (define name (lambda-generator (args ...) body0 bodies ...))]))
>>
>>  (define-generator (blah)
>>                    (for ([x (in-range 0 10)])
>>                         (yield x)))
>>
>>  (printf "test3\n")
>>  (do-tests blah)
>>
>>  #;
>>  (printf "test3\n")
>>  #;
>>  (do-tests (lambda ()
>>              (make-do-sequence (lambda ()
>>                                  (values
>>                                    (lambda (i) i)
>>                                    (lambda (x) (add1 x))
>>                                    0
>>                                    (lambda (x) (< x 10))
>>                                    (lambda (v) #t)
>>                                    (lambda (x v) #t))))))
>>
>>  ;; a cute example
>>  #;
>>  (let ()
>>    ;; generate infinte list of unique names
>>    (define-generator (machine-names)
>>                      (define letters "abcdefghijklmnopqrstuvwxyz")
>>                      (let loop ([len 0])
>>                        ;; generates strings of length num
>>                        (define-generator (generate num)
>>                                          (for ([letter (in-string
>> letters)])
>>                                               (if (= num 0)
>>                                                 (yield (string letter))
>>                                                 (for ([l2 (generate (- num
>> 1))])
>>                                                      (yield (string-append
>> (string letter) l2))))))
>>                        (for ([str (generate len)])
>>                             (yield str))
>>                        (loop (add1 len))))
>>    (for ([s (machine-names)]
>>          [j (in-range 0 1000)])
>>         (printf "~a: ~a\n" j s)))
>>
>>  ))
>>
>> ;; better version of shift/reset using continuation tags
>> ;; also use a unique value to determine the end of the sequence instead of
>> using #f
>> (define test4
>> (let ()
>>  (define-syntax-parameter yield (lambda (stx)
>>                                   (raise-syntax-error #f "yield is only
>> bound inside a sequence generator")))
>>
>>  (define-syntax lambda-generator
>>    (syntax-rules ()
>>      [(_ (args ...) body0 bodies ...)
>>       (lambda (args ...)
>>         (let* ([last (lambda () (void))]
>>                ;; current is a function that invokes user code and
>> produces values
>>                [current (lambda ()
>>                           ;; a unique tag to jump to
>>                           (define tag (make-continuation-prompt-tag))
>>                           ;; give the value to the sequence
>>                           (define next (lambda (value)
>>                                          (shift-at tag f (values value
>> f))))
>>                           (syntax-parameterize ([yield
>> (make-rename-transformer #'next)])
>>                                                (reset-at tag
>>                                                  body0 bodies ...
>>                                                  (values #f last)
>>                                                  )))]
>>                  [pos 0]
>>                  [seq (make-do-sequence (lambda ()
>>                                         (values
>>                                           ;; produce a value and a
>> continuation
>>                                           (lambda (i) (let-values ([(value
>> next) (current)])
>>                                                         ;; set! is ugly
>> but can we do better?
>>                                                         (set! current
>> next)
>>                                                         value))
>>                                           (lambda (x) (add1 x))
>>                                           0
>>                                           (lambda (x) (not (eq? last
>> current)))
>>                                           (lambda (v) (not (eq? last
>> current)))
>>                                           (lambda (x v) (not (eq? last
>> current))))))])
>>           seq))]))
>>
>>  (define-syntax define-generator
>>    (syntax-rules ()
>>      [(_ (name args ...) body0 bodies ...)
>>       (define name (lambda-generator (args ...) body0 bodies ...))]))
>>
>>  (define-generator (blah)
>>                    (for ([x (in-range 0 10)])
>>                         (yield x)))
>>
>>  (printf "test4\n")
>>  (do-tests blah)
>>
>>  (check (for/list ([i ((lambda-generator ()
>>                                         (for ([i (in-range 0 5)]) (yield
>> #f))))])
>>                   i)
>>         (for/list ([i (in-range 0 5)])
>>                   #f))
>>
>>  (check (for/list ([i ((lambda-generator ()
>>                                         (for ([i (in-range 0 5)])
>>                                              (yield
>>                                                (reset
>>                                                  (for ([j (in-range 2 5)])
>>                                                       (shift k (+ i
>> j))))))))])
>>                   i)
>>         (for/list ([i (in-range 2 7)])
>>                   i))
>>
>>  #;
>>  (printf "test3\n")
>>  #;
>>  (do-tests (lambda ()
>>              (make-do-sequence (lambda ()
>>                                  (values
>>                                    (lambda (i) i)
>>                                    (lambda (x) (add1 x))
>>                                    0
>>                                    (lambda (x) (< x 10))
>>                                    (lambda (v) #t)
>>                                    (lambda (x v) #t))))))
>>
>>  ;; a cute example
>>  #;
>>  (let ()
>>    ;; generate infinte list of unique names
>>    (define-generator (machine-names)
>>                      (define letters "abcdefghijklmnopqrstuvwxyz")
>>                      (let loop ([len 0])
>>                        ;; generates strings of length num
>>                        (define-generator (generate num)
>>                                          (for ([letter (in-string
>> letters)])
>>                                               (if (= num 0)
>>                                                 (yield (string letter))
>>                                                 (for ([l2 (generate (- num
>> 1))])
>>                                                      (yield (string-append
>> (string letter) l2))))))
>>                        (for ([str (generate len)])
>>                             (yield str))
>>                        (loop (add1 len))))
>>    (for ([s (machine-names)]
>>          [j (in-range 0 1000)])
>>         (printf "~a: ~a\n" j s)))
>>
>>  ))
>>
>> _________________________________________________
>> For list-related administrative tasks:
>> http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>
> _________________________________________________
>  For list-related administrative tasks:
>  http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>

Posted on the users mailing list.