[plt-scheme] Sequences are no fun

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Tue Jul 1 09:44:04 EDT 2008

At Mon, 30 Jun 2008 17:23:46 -0700, "Mark Engelberg" wrote:
> I'm trying to code up some higher-order functions on sequences, and
> I'm finding this to be quite tricky.  A big part of the problem is
> that there is no good way to "peek" at the head of the sequence
> without consuming it, and another big part of the problem is the sheer
> verbosity of converting sequences to generating functions and back.
> 
> As a case in point, here's what I've go so far for drop-while:
> 
> (define (sequence->list seq) (for/list ((i seq)) i))
> 
> (define (identity x) x)
> (define (const-true x) #t)
> 
> (define (drop-one seq)
>   (let-values ([(more? next) (sequence-generate seq)])
>     (next)
>     (make-do-sequence
>      (λ () (values identity (λ (x) (next)) (next) const-true
> const-true (λ (t v) (more?)))))))
> 
> (define (drop-while pred seq)
>   (let-values ([(more? next) (sequence-generate seq)])
>     (cond
>       [(not (more?)) empty]
>       [else (let ([next-value (next)])
>               (cond
>                 [(pred next-value) (drop-while pred (drop-one seq))]
>                 [else (make-do-sequence
>                        (λ () (values identity (λ (x) (next))
> next-value const-true const-true (λ (t v) (more?)))))]))])))
> 
> 
> This almost works, but a sequence formed by drop-while isn't consumed
> properly by sequence->list (try doing a sequence->list twice on
> something generated by drop-while, and you'll see what I mean).

I think the problem is that `seq' is instantiated too early. It should
be converted to a generator when the sequence produced by `drop-while'
is converted to a generator. That is, `sequence-generate' should be
called within the thunk passed to `make-do-sequence'. That's also the
point where you want to determine the first item (and whether there are
any items at all):

I'd write it like this:

 (define (drop-while pred seq)
   (make-do-sequence
    (lambda ()
      (let-values ([(more? next) (sequence-generate seq)])
        (let loop ()
          (if (more?)
              (let ([val (next)])
                (if (pred val)
                    (loop)
                    ;; sequence that starts with val:
                    (values
                     ;; pos->val:
                     (lambda (mode)
                       (case (car mode)
                         [(init) (cdr mode)]
                         [(get) (next)]))
                     ;; next-pos:
                     (lambda (mode) '(get))
                     ;; initial pos; we put `val' here instead
                     ;; of referring to it directly in the `pos->val'
                     ;; proc so that `val' can be GC'ed after it's
                     ;; used up.
                     (cons 'init val)
                     ;; continue at position?
                     (lambda (mode)
                       (case (car mode)
                         [(init) #t]
                         [(get) (more?)]))
                     ;; continue after val?
                     (lambda (val) #t)
                     ;; continue after pos + val?
                     (lambda (mode val) #t))))
              ;; Empty sequence:
              (values void void #f (lambda (pos) #f) void void)))))))


Matthew



Posted on the users mailing list.