[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

ITERATE



    Date: Tue, 16 Jul 85 15:04:04 PDT
    From: Alex Quilici <alex at UCLA-LOCUS.ARPA>

    I have a question though: do most people consider using
    "catch" and "throw" bad style?  I've been using them with "walk"
    to produce loops that can end early...  Is "iterate" the preferred
    way?  Most people around here are using catch...

I don't know about "most people."  Depends on who you talk to.  My bias,
and that of Sussman and other so-called "purists," is that CATCH is a
terrible thing, like GOTO, and only to be used where absolutely
necessary (generally only in bizarre error-handling situations, the
implementation of read-eval-print loops and user interfaces, and
multitasking).  Some people find loops written with ITERATE or local
functions (which are equivalent) to be opaque, whereas others swear by
them.  People who don't like ITERATE generally prefer some sort of
explicit looping construct like Yale LOOP, Zetalisp LOOP, or whatever.
Mapping functions are nice, but don't always fit the bill, e.g. when
there are multiple exit conditions.  (Anyone out there care to comment
on LOOP vs. the combination of WALK and CATCH?)

ITERATE is deceptively simple.  It's exactly like LET, except that you
can loop around to the top, giving the bound variables new values, by
invoking the named function explicitly.  [In fact, MIT Scheme just reuses
the name LET instead of having a separate ITERATE construct.  I.e. (LET
((...) ...) ...) is the familiar LET, and (LET LOOP ((...) ...) ...) is
like (ITERATE LOOP ((...) ...) ...).]  The nice thing about it is that
you can have as many ways to exit the loop, and as many ways to continue
the loop, as you want; as contrasted e.g. with DO, which only allows for
one exit condition, and always steps the variables in the same way.

(ITERATE LOOP ((VAR1 INIT1) (VAR2 INIT2))
  (COND (EXIT-TEST-1? VALUE-1)
	(EXIT-TEST-2? VALUE-2)
	(TEST? (LOOP VAR1-STEP-1 VAR2-STEP-1))
	(ELSE  (LOOP VAR1-STEP-2 VAR2-STEP-2))))

Also, nested ITERATE's work very nicely; the inner loop can decide to
continue the outer loop, or even to terminate the whole thing.

(ITERATE OUTER ((VAR1 INIT1))
  (COND (EXIT-OUTER? FINAL-VALUE-A)
	(CONTINUE-OUTER-WITHOUT-DOING-INNER? (OUTER STEP1-A))
	(ELSE
	 (ITERATE INNER ((VAR2 INIT2))
	   (COND (CONTINUE-INNER? (INNER STEP2))
		 (CONTINUE-OUTER? (OUTER STEP1-B))
		 (ELSE FINAL-VALUE-B))))))

The bad thing about ITERATE is that information concerning the sequence
of values taken on by induction variables is spread haphazardly through
the code, the initialization and stepping forms not being textually
adjacent, as they are e.g. in DO forms, in the fancier iteration macros,
or when mapping functions are used.  If you want to find out how
a particular variable gets stepped, you have to go hunting through the
entire body.  This is especially bad when there are many induction
variables, or when the loop is at all complicated.

I think the ultimate looping construct has yet to be invented.  APL, and
Waters' LETS, are close.

I don't doubt that the explanation in the manual is too terse to be
generally understood, especially by people not accustomed to programming
in Scheme.

Jonathan