From: Joshua Taylor on
On 2010.06.07 1:30 AM, jwalker wrote:
> On Jun 6, 8:26 pm, Joshua Taylor<tay...(a)cs.rpi.edu> wrote:
>> On 2010.06.06 10:41 PM, jwalker wrote:
>>
>>
>>
>>
>>
>>> Hi,
>>
>>> I understand the basics of macros in LISP.
>>> But I am having a hard time with recursive macros.
>>
>>> Is it possible to write a macro "doforeach", which would be called
>>> like this:
>>> (doforeach list(i1 i2) list(list1 list2) )
>>
>>> and would expand into this?
>>
>>> (foreach i1 list1
>>> (foreach i2 list2
>>> (println "Hello")
>>> )
>>> )
>>
>>> Thanks,
>>> Johannes
>>
>> Sure---remember that macros just receive code (as lisp objects) and
>> produce code (as lisp objects). So if you can construct
>>
>> (foreach i1 list1 (foreach i2 list2 form1 form2 ...)
>>
>> from
>>
>> (i1 i2) and (list1 list2) and (form1 form2 ...)
>>
>> then you can have a macro that does that too. (You'll notice I changed
>> the syntax a little bit. If you really want the macro to look like
>> "(doforeach list (i1 i2) list (list1 list2) ...)" with those "list"s in
>> there, you can do that too. It's not a huge difference from what we're
>> about to do.) For instance, a function that does just that is:
>>
>> (defun make-doforeach (vars list-forms body)
>> (destructuring-bind (var1&rest vars) vars
>> (destructuring-bind (list1&rest list-forms) list-forms
>> (cond
>> ((and (endp vars) (endp list-forms))
>> (list* 'foreach var1 list1 body))
>> ((and (not (endp vars)) (not (endp list-forms)))
>> (list 'foreach var1 list1
>> (make-doforeach vars list-forms body)))
>> (t (error "Same number of variables and list ~
>> forms must be provided."))))))
>>
>> For instance:
>>
>> CL-USER 18> (make-doforeach '(v1 v2 v3) '(l1 l2 l3) '(form1 form2))
>> (FOREACH V1 L1 (FOREACH V2 L2 (FOREACH V3 L3 FORM1 FORM2)))
>>
>> Then the macro is easy to define:
>>
>> (defmacro doforeach (vars list-forms&body body)
>> (make-doforeach vars list-forms body))
>>
>> CL-USER 19> (macroexpand '(doforeach (v1 v2 v3) (l1 l2 l3)
>> form1
>> form2))
>> (FOREACH V1 L1 (FOREACH V2 L2 (FOREACH V3 L3 FORM1 FORM2)))
>> T
>>
>> Is this what you had in mind?
>>
>> //JT
>
> Yes, thanks a bunch!
> This does exactly what I was looking for.
> I was stuck trying for the macro to call itself.
> Hadn't figured out how to use a function instead.

You don't have to use an auxiliary function as I did above, of course.
You can do this all within the macro definition form, provided that all
arguments to foreach after the second are in positions where
macroexpansion will occur:

(defmacro doforeach (vars list-forms &body body)
(destructuring-bind (var &rest vars) vars
(destructuring-bind (list-form &rest list-forms) list-forms
(cond
((and (endp vars) (endp list-forms))
`(foreach ,var ,list-form ,@body))
((and (not (endp vars)) (not (endp list-forms)))
`(foreach ,var ,list-form (doforeach ,vars ,list-forms ,@body)))
(t (error "Same number of variables and list forms must ~
be provided."))))))

Now let's look at the macroexpansion:

CL-USER 20 > (macroexpand '(doforeach (v1 v2 v3) (l1 l2 l3)
form1
form2))
(FOREACH V1 L1 (DOFOREACH (V2 V3) (L2 L3) FORM1 FORM2))
T

Everything hasn't been expanded just yet, but as long as macroexpansion
will occur within (foreach v1 l1 (doforeach ...)) that inner doforeach
will eventually get expanded to (foreach v2 l2 (doforeach ...)) and so
on. In the first example I gave, I used an auxiliary function to make
it clear that we're just manipulating data, and the macro just needs to
return some data that can be understood as code. In practice I'd
probably write this second form rather than the first. (Although, it is
worth noting that, in this second form, more macroexpansions are
necessary to turn the top doforeach form into the eventual expansion.

//JT
From: Thomas A. Russ on
jwalker <johannes.grad(a)gmail.com> writes:

> Hi,
>
> I understand the basics of macros in LISP.
> But I am having a hard time with recursive macros.
>
> Is it possible to write a macro "doforeach", which would be called
> like this:
> (doforeach list(i1 i2) list(list1 list2) )
>
> and would expand into this?
>
> (foreach i1 list1
> (foreach i2 list2
> (println "Hello")
> )
> )

Well, as others have pointed out, a better call would be something like

(doforeach (i1 i2) (list1 list2) form1 form2 ... formN)

If I were writing such a macro, I would use an iterative solution to
build up the forms from the inside out and just return the final
product, something along the lines of

(defmacro doforeach (vars values &body forms)
;; Use PROGN in case we get no vars. Also so we don't
;; have to special case the inner-most form.
(let ((result `(progn ,@forms)))
(assert (= (length vars) (length values)))
(loop for var in (reverse vars)
as val in (reverse values)
do (setq result `(foreach ,var ,val ,result)))
result))

> (macroexpand '(doforeach (i1 i2) (list1 list2) form1 form2 formN))
==>
(FOREACH I1 LIST1 (FOREACH I2 LIST2 (PROGN FORM1 FORM2 FORMN)))


Later on you indicate that instead of an explicit list of variables (and
presumably values?) you want to get them at run time. But that means
you can't use a macro, because the information will not be available at
macro expansion time. It would also mean that you couldn't actually
reference any of your iteration variables inside the forms, because you
wouldn't know what they are until run time either.

If all of the values are only available at run time, then you will be
left with a situation where you could really only create the code you
want (using a function, since this is at run time) and then use either
EVAL on the resulting code or wrap the code in a LAMBDA to feed to
COMPILE and FUNCALL.

--
Thomas A. Russ, USC/Information Sciences Institute
From: RG on
In article <ymifx0y205c.fsf(a)blackcat.isi.edu>,
tar(a)sevak.isi.edu (Thomas A. Russ) wrote:

> jwalker <johannes.grad(a)gmail.com> writes:
>
> > Hi,
> >
> > I understand the basics of macros in LISP.
> > But I am having a hard time with recursive macros.
> >
> > Is it possible to write a macro "doforeach", which would be called
> > like this:
> > (doforeach list(i1 i2) list(list1 list2) )
> >
> > and would expand into this?
> >
> > (foreach i1 list1
> > (foreach i2 list2
> > (println "Hello")
> > )
> > )
>
> Well, as others have pointed out, a better call would be something like
>
> (doforeach (i1 i2) (list1 list2) form1 form2 ... formN)

This is largely a matter of personal taste, but I personally think this
is not a good design because it looks like the form establishes parallel
bindings instead of nested bindings. I would do it this way:

(doforeach
l1 list1
l2 list2
...
ln listn
(form)*
)

And implement it something like:

(defmacro doforeach (&rest body)
(if (symbolp (first body))
`(dolist (,(first body) ,(second body)) (doforeach ,@(cddr body)))
`(progn ,@body)))

rg
First  |  Prev  | 
Pages: 1 2
Prev: mo better qooxlisp
Next: Racket v5.0