Extension of the parameter list in the general lisp macro

I usually try to teach myself lisp, and as an exercise in macro writing, I try to create a macro to define a closed loop of arbitrary depth. I work with sbcl using emacs and slime.

To get started, I wrote this macro with two loops:

(defmacro nested-do-2 (ii jj start end &body body) `(do ((,ii ,start (1+ ,ii))) ((> ,ii ,end)) (do ((,jj ,ii (1+ ,jj))) ((> ,jj ,end)) ,@body))) 

which I could use as follows:

 (nested-do-2 ii jj 10 20 (print (+ ii jj))) 

By the way, I originally wrote this macro using gensym to generate loop counts (ii, jj), but then I realized that the macro was pretty useless if I could not access the counters in the body.

In any case, I would like to generalize the macro to create a nested loop loop that will be nested at an arbitrary level. This is what I have so far, but this does not quite work:

 (defmacro nested-do ((&rest indices) start end &body body) `(dolist ((index ,indices)) (do ((index ,start (1+ index))) ((> index ,end)) (if (eql index (elt ,indices (elt (reverse ,indices) 0))) ,@body)))) 

which I would like to call as follows:

 (nested-do (ii jj kk) 10 15 (print (+ ii jj kk))) 

However, the list does not expand properly, and I ended up in the debugger with this error:

 error while parsing arguments to DEFMACRO DOLIST: invalid number of elements in ((INDEX (II JJ KK))) 

And in case this is not obvious, the point of the built-in if statement should execute the body only in the innermost loop. For me, this does not look very elegant, and it has not been tested (since I could not expand the list of parameters yet), but actually this is not a question of this question.

How can I expand a list correctly in a macro? Is the problem a macro syntax or a list expression in a function call? Any other comments would also be appreciated.

Thanks in advance.

+4
source share
2 answers

Here, one way to do this is to build a structure from the bottom (the body of the loop) up through each index:

 (defmacro nested-do ((&rest indices) start end &body body) (let ((rez `(progn ,@body))) (dolist (index (reverse indices) rez) (setf rez `(do ((,index ,start (1+ ,index))) ((> ,index ,end)) ,rez))))) 
+1
source

[In addition to the votes cast, it really works, and it’s great too!]

To clearly illustrate the recursive nature of the definition of macros, here is a diagram:

 (define-syntax nested-do (syntax-rules () ((_ ((index start end)) body) (do ((index start (+ 1 index))) ((= index end)) body)) ((_ ((index start end) rest ...) body) (do ((index start (+ 1 index))) ((= index end)) (nested-do (rest ...) body))))) 

Using the above as a template, something like this does this:

 (defmacro nested-do ((&rest indices) start end &body body) (let ((index (car indices))) `(do ((,index ,start (1+ ,index))) ((> ,index ,end)) ,(if (null (cdr indices)) `(progn ,@body) `(nested-do (,@(cdr indices)) ,start ,end ,@body))))) * (nested-do (ij) 0 2 (print (list ij))) (0 0) (0 1) (0 2) (1 0) (1 1) (1 2) (2 0) (2 1) (2 2) NIL 

Note that when using all Common-Lisp macros, you need to use gensym templates to avoid capturing the variable.

0
source

Source: https://habr.com/ru/post/1469146/


All Articles