From: ila
Subject: The "do" macro
Date: 
Message-ID: <6f888699-c204-4f1e-adaa-e4ee6ed1c190@p25g2000hsf.googlegroups.com>
I wanted to ask how I can create a macro that is equivalent to "do"
but allows me to use multiple end tests.

For example, I want to be able to type something like this:
(do ((x 2 (+ x 5)) (y 3 (+ y 5)))
     (((> x 7) (setf x "abc") (print "def"))
      ((> y 8) (setf r "deg") (print "jkln")))
  (print "hello"))

So there are more than one end-tests and each test has a corresponding
return value.

Also, I wanted to know where I can find the source code for the "do"
macro. I looked for in in the CVS repository of SBCL but couldn't find
it.

From: Kaz Kylheku
Subject: Re: The "do" macro
Date: 
Message-ID: <7c78332d-4817-431a-944a-3f12acc3f7e3@m3g2000hsc.googlegroups.com>
On Jun 10, 9:53 am, ila <·········@gmail.com> wrote:
> I wanted to ask how I can create a macro that is equivalent to "do"
> but allows me to use multiple end tests.
>
> For example, I want to be able to type something like this:
> (do ((x 2 (+ x 5)) (y 3 (+ y 5)))
>      (((> x 7) (setf x "abc") (print "def"))
>       ((> y 8) (setf r "deg") (print "jkln")))
>   (print "hello"))
>
> So there are more than one end-tests and each test has a corresponding
> return value.

The DO and DO* constructs set up an implicit block named NIL. That is
to say, you can use RETURN within a DO or DO* loop to terminate
immediately, with the specified return value.

This can be used as a basis for writing a wrapper for DO or DO* which
implements your syntax. You simply have to translate this:

 (MY-DO (VAR-INIT-STEP ...)
        ((E1 ... R1)
         (E2 ... R2)
          ...
         (EN ... RN))
   BODY ...)

to this:

 (DO (VAR-INIT-STEP ...)
     ((COND (E1 (RETURN (PROGN ... R1)))
            (E2 (RETURN (PROGN ... R2)))
             ...
            (EN (RETURN (PROGN ... RN)))))
   BODY)

Also, there is always LOOP:

(loop for x from 2 by 5
      for y from 3 by 5
      when (> x 7) do
        (setf x "abc")
        (return (print "def"))
      when (> y 8) do
        (setf r "deg")
        (return (print "jkln"))
      do
        (print "hello"))
From: Alan Crowe
Subject: Re: The "do" macro
Date: 
Message-ID: <86fxrjoq8k.fsf@cawtech.freeserve.co.uk>
Kaz Kylheku <········@gmail.com> writes:
> The DO and DO* constructs set up an implicit block named NIL. That is
> to say, you can use RETURN within a DO or DO* loop to terminate
> immediately, with the specified return value.
> 

which makes it very tempting to just stick WHEN and RETURN
at the top of the body:

CL-USER> (do ((i 0 (+ 1 i)))
             ((= i 10) 'how-the-hell-did-we-get-here?)
           (when (and (= i 5) (y-or-n-p "Five enough? "))
             (return 'five-it-is))
           (when (= i 7)
             (return 'thats-enough)))
             
Five enough? y

FIVE-IT-IS

CL-USER> (do ((i 0 (+ 1 i)))
             ((= i 10) 'how-the-hell-did-we-get-here?)
           (when (and (= i 5) (y-or-n-p "Five enough? "))
             (return 'five-it-is))
           (when (= i 7)
             (return 'thats-enough)))
             
Five enough? n

THATS-ENOUGH

Is a macro worth the bother?

Alan Crowe
Edinburgh
Scotland
From: Pascal Costanza
Subject: Re: The "do" macro
Date: 
Message-ID: <6b7qaaF3b4pjqU1@mid.individual.net>
ila wrote:
> I wanted to ask how I can create a macro that is equivalent to "do"
> but allows me to use multiple end tests.
> 
> For example, I want to be able to type something like this:
> (do ((x 2 (+ x 5)) (y 3 (+ y 5)))
>      (((> x 7) (setf x "abc") (print "def"))
>       ((> y 8) (setf r "deg") (print "jkln")))
>   (print "hello"))
> 
> So there are more than one end-tests and each test has a corresponding
> return value.
> 
> Also, I wanted to know where I can find the source code for the "do"
> macro. I looked for in in the CVS repository of SBCL but couldn't find
> it.

The best way to write macros is to do the following steps:

(1) Write down how you want to express things. (You have already done 
that above.)

(2) Write down what you would have to write to achieve the same thing 
without the macro.

(2a) Sometimes a good idea: Try to explore corner cases, both in what 
you want to write and what you have to write without the macro. Do a few 
iterations here. Depending on programming style, you can do that later.

(3) Write the macro as a function that transforms code from step (1) to 
code from step (2). (That's what macros are.)

Hint: You don't need to reimplement 'do from scratch - it already 
provides 80% of what you need.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: vanekl
Subject: Re: The "do" macro
Date: 
Message-ID: <g2mk7r$bi8$1@aioe.org>
ila wrote:
snip
> Also, I wanted to know where I can find the source code for the "do"
> macro. I looked for in in the CVS repository of SBCL but couldn't find
> it.

defboot.lisp
primordial-extensions.lisp

  find . -iname "*lisp" -print | xargs egrep "def.*\<do\>[^-]" | grep -v tests
  find . -iname "*lisp" -print | xargs egrep "def.*\<frob-do\>" | grep -v tests
From: Pascal J. Bourguignon
Subject: Re: The "do" macro
Date: 
Message-ID: <87y75d867l.fsf@hubble.informatimago.com>
ila <·········@gmail.com> writes:

> I wanted to ask how I can create a macro that is equivalent to "do"
> but allows me to use multiple end tests.
>
> For example, I want to be able to type something like this:
> (do ((x 2 (+ x 5)) (y 3 (+ y 5)))
>      (((> x 7) (setf x "abc") (print "def"))
>       ((> y 8) (setf r "deg") (print "jkln")))
>   (print "hello"))
>
> So there are more than one end-tests and each test has a corresponding
> return value.
>
> Also, I wanted to know where I can find the source code for the "do"
> macro. I looked for in in the CVS repository of SBCL but couldn't find
> it.

So you find it too painful to write:

(do ((x 2 (+ x 5))
     (y 3 (+ y 5)))
    ((COND ((> x 7) (setf x "abc") (print "def")  T)
           ((> y 8) (setf r "deg") (print "jkln") T)
           (T                                     NIL)))
  (print "hello"))

?


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

IMPORTANT NOTICE TO PURCHASERS: The entire physical universe,
including this product, may one day collapse back into an
infinitesimally small space. Should another universe subsequently
re-emerge, the existence of this product in that universe cannot be
guaranteed.
From: Kent M Pitman
Subject: Re: The "do" macro
Date: 
Message-ID: <utzg0uasd.fsf@nhplace.com>
···@informatimago.com (Pascal J. Bourguignon) writes:

> ila <·········@gmail.com> writes:
> 
> > I wanted to ask how I can create a macro that is equivalent to "do"
> > but allows me to use multiple end tests.
> >
> > For example, I want to be able to type something like this:
> > (do ((x 2 (+ x 5)) (y 3 (+ y 5)))
> >      (((> x 7) (setf x "abc") (print "def"))
> >       ((> y 8) (setf r "deg") (print "jkln")))
> >   (print "hello"))
> >
> > So there are more than one end-tests and each test has a corresponding
> > return value.
> 
> So you find it too painful to write:
> 
> (do ((x 2 (+ x 5))
>      (y 3 (+ y 5)))
>     ((COND ((> x 7) (setf x "abc") (print "def")  T)
>            ((> y 8) (setf r "deg") (print "jkln") T)
>            (T                                     NIL)))
>   (print "hello"))

That won't lead to the right thing because it won't return the result
of the exit form.  If you only want the exit form to side-effect it 
is ok.

What it looks like the OP wants is:

(do ((x 2 (+ x 5)) (y 3 (+ y 5)))
    (nil)
  (cond ((> x 7) (return (progn (setf x "abc") (print "def" ))))
        ((> y 8) (return (progn (setf r "deg") (print "jkln")))))
  (print "hello"))

That means something vaguely like the following (untested, good luck):

(defmacro do-multi (bindings test-clauses &body body-expressions)
  `(do ,bindings (nil)
     (cond ,@(mapcar #'(lambda (test-clause)
                         (destructuring-bind (test . value-forms) test-clause
                           `(,test (return (progn ,@value-forms)))))
                     test-clauses))
     ,@body-expressions))
 
> > Also, I wanted to know where I can find the source code for the "do"
> > macro.

You want "some source code".  Or "the source code for SBCL's implementation."
The definite article "the" implies there is only one source.  There is 
probably only one source per-implementation, but CL is defined by a standard,
not by an implementation.  Implementations are permitted to differ in how
they implement the standard.  This differs from how some languages work, where
there is a canonical implementation and the last word on semantics rests with
the implementation.

> > I looked for in in the CVS repository of SBCL but couldn't find it.

I don't use SBCL, by the way, which is why I didn't answer this part.
I'm sure someone will respond about this.
From: ila
Subject: Re: The "do" macro
Date: 
Message-ID: <9e2d9cf5-3ec6-4ed9-a74e-710cb39d1d3b@f63g2000hsf.googlegroups.com>
> That means something vaguely like the following (untested, good luck):
>
> (defmacro do-multi (bindings test-clauses &body body-expressions)
>   `(do ,bindings (nil)
>      (cond ,@(mapcar #'(lambda (test-clause)
>                          (destructuring-bind (test . value-forms) test-clause
>                            `(,test (return (progn ,@value-forms)))))
>                      test-clauses))
>      ,@body-expressions))

Thanks, Mr. Pitman. I tried your code, and that's what I wanted.
Now, I'll just have to understand how it works :)
I understand most of it, except the destructuring-bind, which I will
look up in the Hyperspec.
Anyways, thanks again.
From: Filipe Cabecinhas
Subject: Re: The "do" macro
Date: 
Message-ID: <m2zlptt3ml.fsf@farnsworth.albasani.net>
···@informatimago.com (Pascal J. Bourguignon) writes:

> ila <·········@gmail.com> writes:
>
> So you find it too painful to write:
>
> (do ((x 2 (+ x 5))
>      (y 3 (+ y 5)))
>     ((COND ((> x 7) (setf x "abc") (print "def")  T)
>            ((> y 8) (setf r "deg") (print "jkln") T)
>            (T                                     NIL)))
>   (print "hello"))
>
> ?

Is it really the same?

(do ((x 1 (1+ x)))
    ((cond ((> x 3) (setf y 3) 42 t))))
=> nil

(do ((x 1 (1+ x)))
    ((> x 3) (setf y 3) 42))
=> 42


-- 

  - Filipe Cabecinhas

(defvar real-email
  (apply #'concatenate 'string
         '("filcab" ·@" "gmail" "." "com"))
  "My real email address.")
From: Pascal J. Bourguignon
Subject: Re: The "do" macro
Date: 
Message-ID: <87prqo99se.fsf@hubble.informatimago.com>
Filipe Cabecinhas <······@nospam.com> writes:

> ···@informatimago.com (Pascal J. Bourguignon) writes:
>
>> ila <·········@gmail.com> writes:
>>
>> So you find it too painful to write:
>>
>> (do ((x 2 (+ x 5))
>>      (y 3 (+ y 5)))
>>     ((COND ((> x 7) (setf x "abc") (print "def")  T)
>>            ((> y 8) (setf r "deg") (print "jkln") T)
>>            (T                                     NIL)))
>>   (print "hello"))
>>
>> ?
>
> Is it really the same?

No, but given the example of the OP, I assumed he didn't care about
the result.


> (do ((x 1 (1+ x)))
>     ((cond ((> x 3) (setf y 3) 42 t))))
> => nil
>
> (do ((x 1 (1+ x)))
>     ((> x 3) (setf y 3) 42))
> => 42

Of course.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

CONSUMER NOTICE: Because of the "uncertainty principle," it is
impossible for the consumer to simultaneously know both the precise
location and velocity of this product.
From: D Herring
Subject: Re: The "do" macro
Date: 
Message-ID: <ysWdnVY-U5TAxdLVnZ2dnUVZ_rXinZ2d@comcast.com>
ila wrote:

> Also, I wanted to know where I can find the source code for the "do"
> macro. I looked for in in the CVS repository of SBCL but couldn't find
> it.

In SLIME, move the cursor to the d in "(do ..." and press <Alt>-<.>.
This should take you to defboot.lisp; repeat for frob-do-body to end 
up on primordial-extensions.lisp line 60.

IIUC, that's what you're looking for.

- Daniel