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

Re: loop macro



I guess in an effort to be concise, I didn't give enough information
about out for macro to be clear...

	From: Bernard S. Greenberg <BSG@SCRC-STONY-BROOK.ARPA>

	What are "let", "initially", "eachtime", "in", "on",
	"from", "bind", "being", "finally", in your macro, if
	not connecting words, or am I totally confused?

It seemed to me that the Lisp Machine loop macro uses key words (and I
don't mean : words) not only to introduce loop clauses but also to connect
clauses in some sense (eg "and", "whereas", etc).  What I was trying
to demonstrate was that it's possible to have a loop macro with much
simpler syntax -- each clause is independent and even equal in some
sense in our for macro.

	From: Daniel L. Weinreb <DLW@SCRC-QUABBIN.ARPA>

	Indeed.  Furthermore, the basic syntax you gave is too
	basic for me to understand the syntax of your proposal,
	since, for example, you didn't say what an <iteration-spec>
	looked like.  Furthermore, what does a <condition-spec>
	do; that is, what does "when <form*>" mean?  Does that
	means that the first form following the "when" is tested,
	and the rest are implicit-progn'ed consequents?  What does
	"eachtime" mean and how is it different from "do"?

Let me answer specifically here and then give examples and a fuller
syntax below and see if I can make all this clear...  It's not
really that complicated, but...  (If you don't get this first part
the first time you read it, skip to the examples and syntax and
then go back and read the first stuff again.  I'm trying to put
the most interesting stuff at the top of this letter...)

An <iteration-spec> can be anything like:

	x in list
	y on list
	i from 1 to n by 2

Even though these clauses have key words in them, the clauses have
definite boundaries -- each clause still stands alone.

A <condition-spec>, when <form>* translates into when (progn <form>*)
and the value of the last <form> given is the value tested.  Using
multiple forms in <condition-spec>'s is probably not the best programming
approach, but before we had an eachtime clause, it was sometimes
necessary.  That brings me to the next question.

"Eachtime" clauses are executed every time through the loop before
any of the conditions ("while", "until", "when", "unless", or even
conditions generated by things like "x in list") are done.  The
"do" clause is only done after those tests and when appropriate --
not every time through the loop.  This is suggested by the usual
ordering of the clauses in the for.  Remember that we don't have
multiple bodies.

	It would help a lot to see an example, too.  How would you
	translate this into your "for" construct?  If you can't
	deal with "until" clauses at the end of the loop, say so
	and move it to the front and translate that instead.
	Thanks.

	(loop for a from start to end by 5
	      for b in list
	      do (do-something a b)
	      until (null b))

That would translate to:

(for a from start to end by 5
     b in list
     eachtime (do-something a b)
     until (null b))

It's unusual for in that there's no body, but the default is "do
nil".  What couldn't be done is if that original do up there was
a collect (or almost any other body keyword) instead.  Then the
for macro would have to be changed so that:

(for a from start to end by 5
     b in list
     collect (do-something a b)
     until (null b))

expanded to something like:

	(let ((g0001 list))
	  (do (($$val)
	       (a start (plus a 5))
	       (b (car g0001) (car (setq g0001 (cdr g0001)))))
	      ((or (greaterp a end) (null g0001)) (nreverse $$val))
	      (setq $$val (cons (do-something a b) $$val))
	      (cond ((null b) (return (nreverse $$val))))))

Currently, all the termination conditions are placed in the do
termination.  It could be awkward to have to repeat the value to
be returned if it got complicated (eg multiple values).  The best
alternative would be to move that part out of the do, but then the
finally clause could not reference the do variables.


With that, let me give some more examples and a full syntax for the
current for macro.  These are taken from the Univ of Maryland Franz
Lisp Environment TR (authored by myself, Randy Trigg and Rich Wood).

; This is like (mapc 'patom '(a b c))
(for x in '(a b c) 
     do (msg x))	; print x to standard out with no terpr
abcnil

; This is like (mapcar 'cons '(a b c) '(d e f)).
(for x in '(a b c) 
     y in '(d e f) 
     collect (cons x y))
((a . d) (b . e) (c . f))

; This is like 
;   (mapcan (function (lambda (x) (list x 3))) '(a b c)).
(for x in '(a b c) 
     join (list x 3))
(a 3 b 3 c 3)

; Example of "sum" as well as "when".
(for x in '(a b 3 4 c 5) 
     when (numberp x) 
     sum x)
12

; Example of "destructuring" with "in".
(for (x (y) . z) in '((a (b) c) (d (e) . f) (g (h) i j))
     do (msg x B y B z N))	; the B's say print blanks and the N
     				; says to do terpr (msg is from Franz
				; and case is significant there)
a b (c)
d e f
g h (i j)
nil

; Example of "thereis" keyword.  Notice that the value of the last expression
; is the one used by "thereis".
(for x in '(a b 3 4 c 5) 
     thereis (msg x N)
	     (numberp x))
a
b
3
t

; Shows off "until", "count", and "finally".  Notice the reference to "$$val"
; (contains the result of the for loop) in the "finally" clause.
(for let (a 3) b 
     until (eq (setq a (add1 a)) 6) 
     count (msg a N) 
     finally (list $$val a))
4
5
(2 6)

; Notice here that "bind" variables can reference "let" variables.
(for let (a 3) 
     bind (b a (add1 b)) 
     while (lessp b 6) 
     sum (msg (cons a b) N)
	 b)
(3 . 3)
(3 . 4)
(3 . 5)
12

; Notice the use of "return" to force the loop to execute only once and the
; value of the variable "a" that the "initially" clause sees.
(for let (a 1) 
     initially (msg a N)
     x in '(h i j)
     bind (b a) (a 4) (c 1 (return $$val))
     collect (list a b c x))
1
((4 1 1 h))

; Example of "last".  Causes the value of the last body executed to be returned.
(for x in '(1 2 3)
     while (lessp x 3)
     unless (onep x)
     last x)
2

; Examples of "tcollect" and "tjoin" -- they are like "collect" and "join"
; except they return tconc cells.
(for x in '(a b c)
     tcollect x)
((a b c) c)

(for x in '(a b c)
     tjoin (list x x))
((a a b b c c) c)

; Some examples of "from" and "fromd" - notice that both "to" and "by" are
; optional.
(for n from 0 to 2
     collect n)
(0 1 2)

(for n fromd 0 by 2
     x in '(a b c)
     collect n)
(0 -2 -4)

; Example of "being" -- rebinds x on each iteration
(for being (x 13)
     n from 0 to 1
     do (msg x N)
	(setq x 14))
13
13
nil

; Examples of "eachtime".  Watch out!  It may execute more often than expected.
(for x in nil
     eachtime (msg 'hi N)
     collect x)
hi
nil

(for n from 0 to 1
     eachtime (msg n N)
     collect n)
0
1
2
(0 1)

; Example of "quit" -- allows only one execution of the body.
(for n from 0 to 13
     when (greaterp n 6)
     quit n)
7


Those should give you a pretty good feel for the macro.  Now for
the full syntax.

First, the easiest way to view the syntax is as (for <clause>*)
where <clause> is any of the things you see down below.  The order
in which the various clauses appear below is more intuitive (they're
in the order that they will be executed) than strictly necessary.
(I'm expanding on what I gave in earlier mail; the length will give
you a clue as to why I didn't give all this then...)  "[ ... ]"
means something is optional, "[ ... ]*" or " ... *" means something
can appear 0 or more times, and " ... +" means something should
appear 1 or more times.

	(for [ let <let-var>+ ]*	; <let-var> is <var> or (<var> <init>)
	     [ initially <form>+ ]
	     [ <iteration-spec>+ ]*	; in, on, from, bind, being, etc
	     [ eachtime <form>+ ]*
	     [ <termination-spec>* ]*	; while <form>+ or until <form>+
	     [ <condition-spec>* ]*	; when <form>+ or unless <form>+
	     [ <body-key> <form>+ ]	; <body-key> is do, collect, etc
	     [ finally <form>+ ] )

Wherever you see <form>+, if more than one <form> really does
appear, then a progn is put around the whole thing and the last
value is the one that is used.  Note that multiple let's turn into
successive let's on the outside so that later let's may reference
vars in earlier ones.  Forms appearing in the initially clause and
the <iteration-spec>'s may also reference any let variables.  An
<iteration-spec> can be any of the following:

	<var> in <form>
	<var> on <form>
	<var> from <start> [ to <end> ] [ by <step> ]
		; <step> defaults to 1
	<var> fromd <start> [ to <end> ] [ by <step> ]
		; subtracts <step> each time
	bind <bind-var>+
		; <bind-var> can be <var> or (<var> [ <init> [ <next> ] ]),
		; the latter is like a do variable clause
	being (<var> <value>)+
		; <var> is set to <value> every time, like
		; bind (<var> <value> <value>)

Each of the <var>'s becomes a do iteration <var>.

Throughout the for, you can use $$val to reference the value (maybe
just so far) of the for.

The table below explains what the <body-key>'s may be and the
corresponding value for the for where x1, ... xi, ... xn are the
values of the clause (last <form> in the clause) for the 1st, ...
ith, ... nth execution of the loop.

	<body-key>	$$val

	do		nil
	collect		(list x1 ... xi ... xn)
	join		(nconc x1 ... xi ... xn)
	sum		(plus x1 ... xi ... xn)
	count		n
	always		(and x1 ... xi ... xn)
	never		(and (not x1) ... (not xi) ... (not xn))
	thereis		(or x1 ... xi ... xn)
	last		xn
	tcollect	like collect only a tconc cell
	tjoin		like join only a tconc cell
	quit		x1 (only executes body once), if body never
				executes, then nil

Note that always, never and thereis shortcut if possible -- eg
always will quit and return nil on the first null xi.  They also
do not execute the finally clause if they quit early.


Hope all this makes sense...

				-Liz