Prev: mo better qooxlisp
Next: Racket v5.0
From: Joshua Taylor on 7 Jun 2010 10:07 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 7 Jun 2010 14:00 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 7 Jun 2010 16:04
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 |