Iterators & generators (RE: Real Problems with Python)

Harald Hanche-Olsen hanche at math.ntnu.no
Tue Feb 15 13:53:29 EST 2000


+ Andrew Cooke <andrew at andrewcooke.free-online.co.uk>:

| That finally hammered home to me just what continuations are all about.
| Does anyone have something similarly elegant that shows a coroutine?

Not me, but let me show you what I learned about it, way back in '73.
I had the pleasure of learning assembly language programming at the
feet of a master -- Ole Johan Dahl of Simula fame -- and of course he
just had to teach us about coroutines and how to do them.  It was
amazingly simple and powerful.

I need to tell you a bit about the CDC3300 and the assembly language
used for it (Compass):  There were no such modern amenities as a
stack.  The usual way to code a subroutine was as follows:

FOO       JMP       *
          ...
          JMP,I     FOO

and you would call FOO with this instruction:

          JSR       FOO

JSR stood for "Jump, Save Return" and that is what it did: It computed
the return address (PC+1) and saved it in the address field at address
FOO (indicated by the asterisk above), then jumped to FOO+1.  To
return, FOO just jumps to its own start address, which by now contains
a jump instruction correctly addressed to get back (except we usually
made the jump indirect, as shown, to save a machine instruction).

If you understand this, we're ready for the coroutine example.  All it
takes is three lines of glue, then the coruotines themselves:

FOOBAR    JMP       FOO
BARFOO    JMP       BAR
          JMP,I     FOOBAR

FOO       ...                 (skipping the usual JMP * glue)
          ...
          JSR       FOOBAR    (call BAR)
          ...
          JSR       FOOBAR    (call BAR again)
          ...

BAR       ...                 (skipping the usual JMP * glue)
          ...
          JSR       BARFOO    (call FOO)
          ...
          JSR       BARFOO    (call FOO again)
          ...

To get the whole thing going, just execute, e.g.,

          JMP       FOO

Whenever FOO wants to invoke BAR, it does so via a JSR to the glue at
FOOBAR.  The return address is stored where the address FOO used to
be, and the glue code jumps to BAR.  Then BAR does some work, calls
BARFOO, which stores a new return address in the glue where BAR's
address used to be, then jumps to FOOBAR, which jumps to FOO - no,
there is a new address there now, so it jumps *into* FOO, not to the
top.  And so control is transferred back and forth, and both FOO and
BAR can be written as if *it* is the main program and it's just
calling on its coroutine as if it were a mere subroutine.  Mighty
handy if, as it was in the example task we were given, FOO would need
to consume a stream of data, massage it in some way, and BAR would do
further work on the resulting stream of output data from FOO.

(My code as shown assumes that the pair will go on forever, or until
one of them halts the program.  To allow a controlled return, a
trivial amount of extra JMP * / JSR - type glue code will be needed.)

What a delight to be able to post ancient assembly code on c.l.py...

Geriatrically y'rs,
-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- "There arises from a bad and unapt formation of words
   a wonderful obstruction to the mind."  - Francis Bacon



More information about the Python-list mailing list