lightlike
lightlike

Reputation: 997

Lisp recursive macro problems

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

Answers (2)

Rainer Joswig
Rainer Joswig

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

C. K. Young
C. K. Young

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

Related Questions