Tutorial on writing 'yield' [Was: Re: [plt-scheme] question about continuation example in "t-y-scheme in fixnum days"]

From: nusret (nbalci_l at yahoo.com)
Date: Tue Apr 4 03:49:54 EDT 2006

Hi Danny,
I couldn't thank you enough! I read very carefully
every single word, and I (hopefully ;)) understand
all, no further questions ;). Not only the question I
asked, but I learned a lot of other stuff: Indeed,
this was a very enjoyable read!

It took me a while to digest the material, but it was
really worth it. After this post, I also placed an
order for the complete "... Schemer" series by
Friedman ;), so he owes you ;).

Thank you so much!

Regards,

Nusret





--- Danny Yoo <dyoo at hkn.eecs.berkeley.edu> wrote:

> 
> 
> On Mon, 3 Apr 2006, nusret wrote:
> 
> > I'm learning Scheme and got a little bit confused
> after reading the tree
> > traversal example on Ch 13, "Jumps" of Teach
> yourself scheme in fixnum
> > days. (page 53 in the pdf file).
> 
> 
> Hi nusret,
> 
> 
> [Note: most of this comes from The Little Schemer;
> you might want to take
> a look at that book sometime: it covers this more
> comprehensively.
> 
> This post is a bit long; apologies!]
> 
> 
> 
> Perhaps it might help if we simplify the example to
> something slightly
> simpler: let's say that we'd like to write a
> generator to march through a
> list of items.  That is, let's say that we'd like
> some function called
> LIST->GENERATOR that behaves like this:
> 
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> > (define elt-generator
>     (list->generator '(hello world) (lambda ()
> 'done)))
> > (elt-generator)
> 'hello
> > (elt-generator)
> 'world
> > (elt-generator)
> 'done
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> 
> 
> How can we go about doing this?  Here's one
> approach:
> 
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (module generator-example mzscheme
>   (provide list->generator)
>   (require (lib "list.ss"))
> 
>   ;; list->generator: (listof X) (-> Y) -> (union X
> Y)
>   ;; returns a generator function that, when called,
> returns successive
>   ;; elements of the list.  When the list is
> exhausted, returns the
>   ;; results of applying finished-thunk.
>   (define (list->generator list finished-thunk)
>     (letrec ([entry-point
>               (lambda ()
>                 (cond
>                   [(restoring-from-save?)
> (continue-from-restore-point!)]
>                   [else (do-work list)]))]
>              [saved-point (void)]
>              [restoring-from-save? (lambda () (not
> (void? saved-point)))]
>              [continue-from-restore-point! (lambda
> () (saved-point))]
>              [save-point! (lambda (f) (set!
> saved-point f))]
>              [do-work
>               (lambda (list)
>                 (cond
>                   [(empty? list) (finished-thunk)]
>                   [else
>                    (save-point! (lambda () (do-work
> (rest list))))
>                    (first list)]))])
>       entry-point)))
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> 
> We're not really using continuations yet, although
> we'll start to
> introduce it soon.  *grin*
> 
> But I hope the rest of the code is clear: we're
> iterating across the list,
> and right before we return elements back, we record
> what we need to do
> next, so that when we call the generator back, the
> entry-point can pick up
> where it left off.
> 
> Let's try it out:
> 
> ;;;;;;
> > (define elts (list->generator '(hello world)
> (lambda () 'done)))
> > (list (elts) (elts) (elts))
> (hello world done)
> ;;;;;;
> 
> 
> Ok, so it works.  But now let's slowly introduce
> elements of continuations
> into the system and see how far we can take this.
> 
> 
> First, let's use what we already know about
> "escaping" continuations and
> modify the entry point slightly, so that results
> that we compute are
> explicitely thrown up to the caller:
> 
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>   (define (list->generator list finished-thunk)
>     (letrec ([entry-point
>               (lambda ()
>                 (call/cc (lambda (k)
>                            (set! caller k)
>                            (cond
>                              [(restoring-from-save?)
>                              
> (continue-from-restore-point!)]
>                              [else (do-work
> list)]))))]
>              [caller (void)]
>              [saved-point (void)]
>              [restoring-from-save? (lambda () (not
> (void? saved-point)))]
>              [continue-from-restore-point! (lambda
> () (saved-point))]
>              [save-point! (lambda (f) (set!
> saved-point f))]
>              [do-work
>               (lambda (list)
>                 (cond
>                   [(empty? list) (caller
> (finished-thunk))]
>                   [else
>                    (save-point! (lambda () (do-work
> (rest list))))
>                    (caller (first list))]))])
>       entry-point))
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> Here, we've made changes to ENTRY-POINT and DO-WORK,
> but this should still
> have the same effect as before.  do-work uses CALLER
> to throw values back,
> but otherwise, this shouldn't be too inescapable. 
> *grin*
> 
> 
> Next, let's look at DO-WORK:
> 
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>              [do-work
>               (lambda (list)
>                 (cond
>                   [(empty? list) (caller
> (finished-thunk))]
>                   [else
>                    (save-point! (lambda () (do-work
> (rest list))))
>                    (caller (first list))]
>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> 
> This almost looks like a simple list traversal using
> what we understand
> about structural recursion, except for the funky
> call to SAVE-POINT, where
> we're explicitely building a lambda that tells the
> system what to do next.
> 
> 
> Wouldn't it be nice if we didn't have to explicitely
> code that
> 
>     (lambda () (do-work (rest list)))
> 
> part?  It turns out that we can, if we take
> advantage of call/cc:
> 
>              [do-work
>               (lambda (lst)
>                 (cond
>                   [(empty? lst) (caller
> (finished-thunk))]
>                   [else
>                    (call/cc (lambda (k)
>                               (save-point! k)
>                               (caller (first lst))))
>                    (do-work (rest lst))]))]
> 
> 
=== message truncated ===


__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 


Posted on the users mailing list.