Prev: mo better qooxlisp
Next: Racket v5.0
From: jwalker on 6 Jun 2010 22:41 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
From: Joshua Taylor on 6 Jun 2010 23:26 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
From: jwalker on 7 Jun 2010 01:30 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. A minor issue now is that I actually need to call the macro with a variable "indices", rather than the actual list: indices=(list 'v1 'v2 'v3) In my implementation, make-doforeach then only sees "indices", rather than the actual list, for its "vars" argument. But that may also be a problem with my environment, I am using a domain-specific-language based on Scheme. Thanks again, Johannes
From: Paul Donnelly on 7 Jun 2010 04:38 jwalker <johannes.grad(a)gmail.com> writes: > A minor issue now is that I actually need to call the macro with a > variable "indices", rather than the actual list: > indices=(list 'v1 'v2 'v3) > > In my implementation, make-doforeach then only sees "indices", rather > than the actual list, for its "vars" argument. That's macros for you. They're expanded at compile time, so of course you can't use information that will only be available at run time.
From: Captain Obvious on 7 Jun 2010 07:22
j> In my implementation, make-doforeach then only sees "indices", rather j> than the actual list, for its "vars" argument. j> But that may also be a problem with my environment, I am using a j> domain-specific-language based on Scheme. No, that's how it should work. Macros work before code is run. (You can think it happens at compile time.) Functions work in run time. So if you want something which works in run time, you need functions, not macros. |