I am trying to write a macro in lisp that returns the nth expression passed into it and only evaluates that expression. For example:
(let ((n 2))
(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))
should return 3. I am getting a divide by 0 error. My macro definition is as follows:
(defmacro nth-expr (n &rest expressions)
(let ((i (gensym))
(expr (gensym)))
`(do ((,i 1 (1+ ,i))
(,expr ,expressions (cdr ,expr)))
((= ,i ,n) (car ,expr)))))
Any idea what I'm doing wrong? Thanks.
EDIT:
Thanks to @Vsevolod Dyomkin for helping me with the above part. Now there is one more problem. When I try to do
(let ((n 3) (x "win") (y "lose"))
(nth-expr n (princ x) (princ x) (princ y) (princ x)))
I am getting the error Error: Attempt to take the value of the unbound variable 'Y'
.
My updated code looks like this:
(defmacro nth-expr (n &rest expressions)
(let ((i (gensym))
(expr (gensym))
(num (gensym)))
`(let ((,num (eval ,n)))
(do ((,i 1 (1+ ,i))
(,expr ',expressions (cdr ,expr)))
((= ,i ,num) (eval (car ,expr)))))))
The main thing is to FIRST come up with the expansion.
What should the working code look like? The code that the macro usage would expand to?
Then you write the macro to create such code.
Make sure that you don't eval any of the supplied code in the macro.
A simple useful expansion target for your problem is a CASE form.
(case n
(0 (do-this))
(1 (do-that))
(2 (do-something-else)))
Now it should be easy to write a macro which expands (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))
into a CASE form...
You do not need eval
here, and no need in storing expressions in a list.
A proper implementation is following:
(defmacro nth-expr (n &rest expressions)
`(case ,n
,@(loop for e in expressions
for n from 1
collect
`((,n) ,e))))
Your example expands as:
(CASE N ((1) (/ 1 0)) ((2) (+ 1 2)) ((3) (/ 1 0)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With