Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lisp macro evaluating expressions when I do not want it to

Tags:

macros

lisp

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)))))))
like image 498
Daniel Avatar asked Nov 30 '11 07:11

Daniel


2 Answers

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...

like image 80
Rainer Joswig Avatar answered Sep 22 '22 21:09

Rainer Joswig


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)))
like image 33
SK-logic Avatar answered Sep 25 '22 21:09

SK-logic