From: Douglas J Roberts
Subject: A CL iteration macro, "while".
Date: 
Message-ID: <13639@beta.UUCP>
I was poking around in the source for Stallman's Gnu Emacs the other day
and stumbled across a "while" function that I kind of liked. It's not
Common nor Zeta LISP, and so I'd never seen it before. We have
Symbolic's big loop macro installed on all of our Common LISP machines
(Suns, TI Explorers, & Symbolics), but even with it there is no clean
way to do simple iteration control that I really like. (I don't like the
syntax of do, dotimes, dolist, etc.)

I thought it would be fun to write a CL while macro and then post it
for comment, etc. I'd be curious for anybody interested enough to
suggest other ways of writing it.

Here 'tis.


(defmacro while (test-form &rest forms)
  "This macro evaluates test-form, and if the result is non-nil
	all subsequent forms will be iteratively evaluated until
	test-form evaluates to nil."
  (prog ()
     again 
	(cond (
	       (eval test-form) 
	       (mapcar #'eval forms)
	       (go again)
	       ))
  ))


Example of use:

(setq *number* 0)


(while (< *number* 5)
       (print *number*)
       (print "line 1")
       (print "line 2")
       (print "line 3")
       (setq *number* (1+ *number*))
       )




-- 
---------------------------------------------------------------
			Doug Roberts
			····@lanl.gov
---------------------------------------------------------------

From: Barry Margolin
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <14171@think.UUCP>
In article <·····@beta.UUCP> ····@beta.UUCP (Douglas J Roberts) writes:
>We have
>Symbolic's big loop macro installed on all of our Common LISP machines
>(Suns, TI Explorers, & Symbolics), but even with it there is no clean
>way to do simple iteration control that I really like.

What's wrong with
	(LOOP WHILE <test-form> DO <body>)
 ?  It's only two words more than the WHILE macro.

>
>I thought it would be fun to write a CL while macro and then post it
>for comment, etc. I'd be curious for anybody interested enough to
>suggest other ways of writing it.

Well, if I were to write it, I would write it in a way that works
correctly.  Your version has two major mistakes: 1) it executes the
form at macro-expansion time, rather than returning the expansion
(i.e. where is the BACKQUOTE?); 2) it uses EVAL, which will cause
expressions to be evaluated in the wrong lexical environment.
>
>(defmacro while (test-form &rest forms)
>  "This macro evaluates test-form, and if the result is non-nil
>	all subsequent forms will be iteratively evaluated until
>	test-form evaluates to nil."
>  (prog ()
>     again 
>	(cond (
>	       (eval test-form) 
>	       (mapcar #'eval forms)
>	       (go again)
>	       ))
>  ))

Correct implementation:

(defmacro while (test-form &body forms)
  `(loop (if ,(test-form)
	     (progn .,forms)
	     (return))))

or, to use your general structure:

(defmacro while (test-form &body forms &aux (tag (gensym)))
  `(tagbody				;instead of PROG, since no local vars
     ,tag
       (cond (,test-form
	      ,@forms
	      (go ,tag)))))

I used the gensym'ed tag so that this can be used inside a TAGBODY or
PROG that has its own tag named AGAIN.  My two versions have a slight
difference: if the supplied body contains a RETURN form it will just
exit the loop in the first case, while in the second case it will exit
the containing block.  The TAGBODY version could be made like the LOOP
version by adding a (BLOCK NIL ...) wrapper.

---
Barry Margolin
Thinking Machines Corp.

······@think.com
seismo!think!barmar
From: David M. Neves
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <4935@spool.cs.wisc.edu>
[[When I see articles that have obvious errors I usually wait a few
days because I know that others will most likely post responses.  In
this case I happened to have defined a while macro myself.  So...]]

In article <·····@beta.UUCP> ····@beta.UUCP (Douglas J Roberts) writes:
>
...
>
>I thought it would be fun to write a CL while macro and then post it
>for comment, etc. I'd be curious for anybody interested enough to
>suggest other ways of writing it.
>
...
>
>(defmacro while (test-form &rest forms)
>  "This macro evaluates test-form, and if the result is non-nil
>	all subsequent forms will be iteratively evaluated until
>	test-form evaluates to nil."
>  (prog ()
>     again 
>	(cond (
>	       (eval test-form) 
>	       (mapcar #'eval forms)
>	       (go again)
>	       ))
>  ))
There are a couple of problems with this code.
1.  You use mapcar to iterate through the forms.  Mapcar is going to
create a list as a result.  Since you are not using the result of
mapcar you have done unnecessary CONSing.  This means your program will
run slower because it will do more garbage collection.  Use mapc
instead. 
2.  Even more serious is the way you defined the macro.  You are
almost using it as we used to use fexprs or nlambdas[1].  A macro
translates its input into another form, which then is evaluated.  Your
code will work if it is interpreted but not when it is compiled.  The
compiler will substitute the translation (in this case it will be nil,
the value of the prog) for each call to "while". 

[1] (A reason for using macros rather than fexprs is to get the
correct scoping when the forms are evaluated.  I won't go more into
this here.)

Here is a correct definition of while in Common Lisp.

(defmacro while (test &rest body)
  `(do nil (,test) ,@body))

"`" is the backquote character.  "," evaluates the s-expression after
it (within a backquote).  ",@" is similar to "," in that it evaluates
the s-expression that follows it.  It is different in that the value
of the s-expression is spliced into the existing list.



David Neves, Computer Sciences Department, University of Wisconsin-Madison
Usenet:  {rutgers,ucbvax,ihnp4}!uwvax!neves
Arpanet: ·····@cs.wisc.edu
From: Cesar Quiroz
Subject: Re: A CL iteration macro, while.
Date: 
Message-ID: <5497@sol.ARPA>
Sender:

Followup-To:


Douglas Roberts (····@beta.UUCP, or is it @lanl.gov?) proposes `a
little non-flamable fun' in the way of asking for a CL "while"
macro, a la GNU Emacs Lisp.  This note offers some comments and a
second implementation.

First of all, Douglas's version:
:
:(defmacro while (test-form &rest forms)
:  "This macro evaluates test-form, and if the result is non-nil
:	all subsequent forms will be iteratively evaluated until
:	test-form evaluates to nil."
:  (prog ()
:     again 
:	(cond (
:	       (eval test-form) 
:	       (mapcar #'eval forms)
:	       (go again)
:	       ))
:  ))

1-  My first observation is that a macro should return a form to be
    later evaluated.  `While' does all its job during
    macro-expansion (in this, it works as a function!).  Beware of
    this, as this is the first thing to know about macros.  For
    instance, a compiler trying to expand a call to `while' will
    need to know how `test-form' will evaluate!
    
    So, let me replace the macro above with:
    (defmacro while-2 (test-form &rest forms)
    "This macro evaluates test-form, and if the result is non-nil
 	all subsequent forms will be iteratively evaluated until
 	test-form evaluates to nil."
    `(prog ()
      again 
 	(cond (
 	       (eval ,test-form) 
 	       (mapcar #'eval ,forms)
 	       (go again)
 	       ))
     ))

2-  CL's eval processes its argument in a *null* lexical
    environment!  Neither `while' nor `while-2' will do their job
    correctly if you intend to control the value of test-form by
    changing local, lexical, bindings.  In general, a good heuristic
    is to feel nervous about a function that calls `eval' directly.
    There are good uses for eval, of course (for instance, when
    writing interpreters for languages embedded in Lisp).

3-  This is a matter of taste and not an absolute rule:  I prefer to
    use mechanisms that are `minimal' to the problem at hand.
    Perhaps `tagbody' is more adequate here.  However, I must grant
    that `prog' permits one to escape the loop with `return'.  I
    leave it to others to criticize such escapes.

4-  A nit: 'again' might be used in one of the forms!  A macro
    should fabricate these local names via gensym or somesuch.
    Imagine the surprise of a user of the macros above when
    discovering that
            (tagbody
               ... stuff ...
             again
               ... more stuff ...
               (while (...)
                  ...
                  (if (something-or-another-p)
                      ;; leave this loop, restart things
                      (go again)) ...))
    doesn't do the obvious.  (Such user, of course, would be guilty
    of horrible style, but I feel he should still be entitled to do
    the kludge above.)

My turn.  Let me propose the following macro in the spirit of
Douglas's posting:
    (defmacro while-3 (test &body forms)
      "[macro] (WHILE TEST &BODY FORMS)
    Evaluate FORMS repeatedly, while TEST evals to true"
      `(do () ((not ,test)) ,@forms))

The idea here is to use the macro to cover the details of `do', not
to reinvent them afresh.  (For instance, I often have trouble
remembering if the condition in the second subform of do/do* is the
condition to stop or the condition to continue iterating.  The macro
above protects the user from such confusions.)

Happy Holidays!
-Cesar


    


-- 
Cesar Augusto  Quiroz Gonzalez
Department of Computer Science     ...allegra!rochester!quiroz
University of Rochester            or
Rochester,  NY 14627               ······@cs.rochester.edu
From: David Loewenstern
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <3503@whuts.UUCP>
In article <·····@beta.UUCP>, ····@beta.UUCP (Douglas J Roberts) writes:
> 
> I was poking around in the source for Stallman's Gnu Emacs the other day
> and stumbled across a "while" function that I kind of liked. It's not
> Common nor Zeta LISP, and so I'd never seen it before. We have
> Symbolic's big loop macro installed on all of our Common LISP machines
> (Suns, TI Explorers, & Symbolics), but even with it there is no clean
> way to do simple iteration control that I really like. (I don't like the
> syntax of do, dotimes, dolist, etc.)
I certainly don't blame you.  DOTIMES and DOLIST don't bother me, but DO
is REALLY ugly -- not much better than using PROG.
> 
> I thought it would be fun to write a CL while macro and then post it
> for comment, etc. I'd be curious for anybody interested enough to
> suggest other ways of writing it.
> 
> Here 'tis.
> 
> 
> (defmacro while (test-form &rest forms)
>   "This macro evaluates test-form, and if the result is non-nil
> 	all subsequent forms will be iteratively evaluated until
> 	test-form evaluates to nil."
>   (prog ()
>      again 
> 	(cond (
> 	       (eval test-form) 
> 	       (mapcar #'eval forms)
> 	       (go again)
> 	       ))
>   ))
This macro performs the loop on expansion.  It cannot be usefully compiled, 
since it always expands to NIL.  Try calling the following function *twice*:
(DEFUN X (n)
   (WHILE (PLUSP n)
      (WHEN (ODDP n) (PRINT n))
      (DECF n)))

I predict: typing (X 5) will print
5
3
1
NIL
and typing (X 5) again will print
NIL
certainly this is the standard behavior on the Symbolics and Explorers.

What is wrong with
  (LOOP while (< *number* 5)
        do (print *number*)
           ...
           (setq *number* (1+ *number*)))

?  In any event, what you probably meant was

(DEFMACRO While (test &BODY body)
  `(PROG ()
     AGAIN
          (WHEN ,test ,@body (GO AGAIN))))

except that this doesn't do anything more than the LOOP macro which you
already have.

> 			Doug Roberts
> 			····@lanl.gov



-- 
David Loewenstern                     
Bangpath: {rutgers!moss,ihnp4}!whuts!davel
          or -- in extremis -- ········@paul.rutgers.edu
Disclaimer: My opinions are controlled by the Bavarian Illuminati, not by AT&T.
From: ·····@dcl-cs.UUCP
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <464@dcl-csvax.comp.lancs.ac.uk>
In article <····@whuts.UUCP> ·····@whuts.UUCP (David Loewenstern) writes:
>In article <·····@beta.UUCP>, ····@beta.UUCP (Douglas J Roberts) writes:
>> 
>> I was poking around in the source for Stallman's Gnu Emacs the other day
>> and stumbled across a "while" function that I kind of liked. 

Yes, that's nice; but there's a nicer. How about this, which is Arthur
Norman's loop macro:

	(loop
		<any number of arbitrary statements>
		(until <exit-condition> <after-exit statements>)
		<any number of arbitrary statements>
		(while <continue-condition> <after-exit statements>)
		<any number of arbitrary statements>)

The 'while' and 'until' conditions can appear any number of times,
(including zero) anywhere in the body of the loop. The flexibility of this
is wonderful - and I'd love to see the source code for it!

It appears in Acornsoft's lisp for the BBC Micro and their more recent
lisp for the new acorn risc machine (Archimedes); so you lot west of the
water may not have seen it. But it's a lovely construct! [Obviously, it
returns the value of the last of the after-exit statements of the until-
or while- statement which allowed exit]

Simon Brooke		·····@uk.ac.lancs.comp
From: Richard A. O'Keefe
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <544@cresswell.quintus.UUCP>
In article <···@dcl-csvax.comp.lancs.ac.uk>, ·····@comp.lancs.ac.uk (Simon Brooke) writes:
> In article <····@whuts.UUCP> ·····@whuts.UUCP (David Loewenstern) writes:
> Yes, that's nice; but there's a nicer. How about this, which is Arthur
> Norman's loop macro:
> 
> 	(loop	~exprs~
> 	    (until <exit-condition> <after-exit statements>)
> 	    ~exprs~
> 	    (while <continue-condition> <after-exit statements>)
> 	    ~exprs~
>	)
> It appears in Acornsoft's Lisp for the BBC Micro and their more recent
> Lisp for the new Acorn RISC machine (Archimedes); so you lot west of the

A very similar construct was published in BIT (I think) in the early 70s.
That construct had
	(REPEAT . forms)
where among the forms you could have
	    WHILE expr	- if expr => NIL, returns NIL from REPEAT
	    UNTIL expr	- if expr => non-NIL, returns it from REPEAT
Note that there are no parens around WHILE or UNTIL.
It wasn't that hard to expand:

	(defmacro REPEAT (&rest forms)
	    `(PROG () L ,@(expand-REPEAT-body forms)))

	(defun expand-REPEAT-body (forms)
	    (cond
		((null forms)
		    `((GO L)))
		((eq (car forms) 'WHILE)
		    `((COND ((NOT ,(cadr forms)) (RETURN NIL)))
			,@(expand-REPEAT-body (cddr forms)) ))
		((eq (car forms) 'UNTIL)
		    `((LET ((X ,(cadr forms)))
			(COND (X (RETURN X)))
			,@(expand-REPEAT-body (cddr forms)) )) )
		(t
		    `(,(car forms)
			,@(expand-REPEAT-body (cdr forms)) )) ))

[This has been tested in Xlisp 1.6]
The original article was actually proposing the construct for general
use, but chose Lisp as a handy specification language.

The trouble with all of this is that we each end up with our own language.
In an Interlisp project, I had my own (while ...) (until ...) (when ...)
and (unless ...) macros.  This was ok until we added another programmer,
who didn't want to load all of my environment just so that he could use
the parts he was supposed to be working on.  (There were other problems.)
Let's face it, none of the macros various people like is enough of an
improvement on FOR or loop to pay for the confusion in multi-programmer
projects.  (LETS would be.  Is there a PD version?)
From: Simon Brooke
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <469@dcl-csvax.comp.lancs.ac.uk>
In article <···@cresswell.quintus.UUCP> ··@quintus.UUCP (Richard A. O'Keefe) writes:

>The trouble with all of this is that we each end up with our own language.
>In an Interlisp project, I had my own (while ...) (until ...) (when ...)
>and (unless ...) macros.  This was ok until we added another programmer,
>who didn't want to load all of my environment just so that he could use
>the parts he was supposed to be working on.  (There were other problems.)
>Let's face it, none of the macros various people like is enough of an
>improvement on FOR or loop to pay for the confusion in multi-programmer
>projects. 

This is fair comment. The major problem with an arbitrarily expandable
language like LISP is that we all end up with our own incompatible
variants. But at the same time, if the language is to continue to develop,
the good variants need to be publicised, so that they can be incorporated
into new standards as they develop. 

Iteration does not fit naturally into LISP; and the development of
iterative constructs has lead to some of the ugliest and murkiest bits of
the language. But we deal with hardware which (generally speaking) is more
efficient at iteration than at recursion. So it is important that we
continue to think about the iterative constructs that we use.

Gosh, doesn't that sound pompous! :-={ (Note stiff upper lip)

Simon Brooke
From: Jeff Dalton
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <228@aiva.ed.ac.uk>
In article <···@dcl-csvax.comp.lancs.ac.uk> ·····@comp.lancs.ac.uk
(Simon Brooke) writes:
>Yes, that's nice; but there's a nicer. How about this, which is Arthur
>Norman's loop macro:

>	(loop
>		<any number of arbitrary statements>
>		(until <exit-condition> <after-exit statements>)
>		<any number of arbitrary statements>
>		(while <continue-condition> <after-exit statements>)
>		<any number of arbitrary statements>)

In ordinary Common Lisp, you can do something similar:

	(loop <form> ...
	      (when <exit-condition>
	            <exit form> ...
	            (return <last exit form>))
	      <form> ...
	      (unless <continue-condition>
	              <exit form> ...
	              (return <last exit form>))
	      <form ...>)

>The 'while' and 'until' conditions can appear any number of times,
>(including zero) anywhere in the body of the loop. The flexibility of this
>is wonderful - and I'd love to see the source code for it!

I think it's a special form (not a macro) in Acorn's Lisp for the
BBC micro, so it's not as portable as we might like.  But it's
fairly easy to code.  You could process loop bodies, as in Richard
O'Keefe's message about REPEAT, or use MACROLET thus:

;;; Arthur Norman's loop macro

(defmacro loop (&body body)
  (let ((label (gensym)))
    `(macrolet
	 ((while (test &body exit-forms)
	    `(if (not ,test) (return (progn ,@exit-forms))))
	  (until (test &body exit-forms)
	    `(if ,test (return (progn ,@exit-forms)))))
       (block nil
	 (tagbody
	   ,label (progn . ,body)
	          (go ,label))))))

Unfortunately, the macrolet provides somewhat excessive generality.
(The WHILEs and UNTILs don't have to be at the top level of the
loop.)

Jeff Dalton,                      JANET: ········@uk.ac.ed             
AI Applications Institute,        ARPA:  ·················@nss.cs.ucl.ac.uk
Edinburgh University.             UUCP:  ...!ukc!ed.ac.uk!J.Dalton
From: ······@iuvax.cs.indiana.edu
Subject: Re: A CL iteration macro, "while".
Date: 
Message-ID: <20700003@iuvax>
>>>>>>>A very similar construct was published in BIT (I think) in the early 70s.

BIT 15 (Dec. 1975), 431-451.