Reputation: 997
I'm writing a recursive Lisp macro taking a number n and evaluating the body n times (an exercise from ANSI Lisp). I've tried two ways of doing this -- having the macro call itself in its expansion, and having the macro expand into a local recursive function. Neither works as I want it to.
Here's the first one -- it has a stack overflow, but when I look at its expansion using macroexpand-1 it seems fine to me.
(defmacro n-times (n &rest body)
(let ((i (gensym)))
`(let ((,i ,n))
(if (zerop ,i) nil
(progn
,@body
(n-times (- ,i 1) ,@body))))))
Here's the second one -- it makes an error, "undefined function #xxxx called with arguments (z)" where #xxxx is the name of the gensym and z is 1 less than the number I call it with. I think there's a problem with the way I use gensyms and flet together, but I'm not sure how to do this correctly.
(defmacro n-times (n &rest body)
(let ((g (gensym)))
`(flet ((,g (x)
(if (zerop x) nil
(progn
,@body
(,g (- x 1))))))
(,g ,n))))
Upvotes: 2
Views: 546
Reputation: 139411
Since macro expansion in Common Lisp happens kind of before runtime, this is slightly tricky.
Remember, the macro sees source code. This means:
the number n must be passed as a number, not a variable, when you use the macro. Thus at macro expansion time the number is known. For such a macro, I would check that in the macro - otherwise you always be tempted to write something like (let ((n 10)) (n-times n ...))
- which won't work.
the macro needs to compute the recursive iteration. Thus the logic is in the macro, and not in the generated code. Each macro needs to generate code, which is one step simpler at macro expansion time - until the basic case is reached.
Upvotes: 7
Reputation: 223193
To answer your first question, you have a recursive macro expansion that never stops recursing. The presence of the if
doesn't stop the recursive macro expansion, since macro expansion happens at compile-time and your if
happens at run-time.
To answer your second question, you can't use flet
to specify recursive functions, you have to use labels
instead.
Upvotes: 9