Xref: utzoo comp.lang.lisp:623 comp.emacs:2440 Path: utzoo!mnetor!uunet!husc6!think!barmar From: barmar@think.COM (Barry Margolin) Newsgroups: comp.lang.lisp,comp.emacs Subject: Re: A CL iteration macro, "while". Message-ID: <14171@think.UUCP> Date: 22 Dec 87 21:54:35 GMT References: <13639@beta.UUCP> Sender: usenet@think.UUCP Reply-To: barmar@sauron.think.com.UUCP (Barry Margolin) Organization: Thinking Machines Corporation, Cambridge, MA Lines: 63 Keywords: Let's have a little non-flamable fun... In article <13639@beta.UUCP> dzzr@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 DO ) ? 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. barmar@think.com seismo!think!barmar