Lisp макрос оценивает выражения, когда я не хочу - PullRequest
5 голосов
/ 30 ноября 2011

Я пытаюсь написать макрос на lisp, который возвращает переданное в него n-е выражение и оценивает только это выражение. Например:

(let ((n 2))
  (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))

должен вернуть 3. Я получаю ошибку деления на 0. Мое определение макроса следующее:

(defmacro nth-expr (n &rest expressions)
  (let ((i (gensym))
        (expr (gensym)))
    `(do ((,i 1 (1+ ,i))
          (,expr ,expressions (cdr ,expr)))
         ((= ,i ,n) (car ,expr)))))

Есть идеи, что я делаю не так? Спасибо.

EDIT:

Спасибо @Vsevolod Dyomkin за помощь в выполнении вышеуказанной части. Теперь есть еще одна проблема. Когда я пытаюсь сделать

(let ((n 3) (x "win") (y "lose"))
  (nth-expr n (princ x) (princ x) (princ y) (princ x)))

Я получаю ошибку Error: Attempt to take the value of the unbound variable 'Y'.

Мой обновленный код выглядит так:

(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)))))))

Ответы [ 4 ]

5 голосов
/ 30 ноября 2011

Главное, чтобы FIRST придумал расширение.

Как должен выглядеть рабочий код?Код, который расширит использование макроса?

Затем вы напишите макрос для создания такого кода.

Убедитесь, что вы не проверяете ни один из предоставленных кодов в макросе.

Простая полезная цель расширения для вашей проблемы - форма CASE.

(case n
  (0 (do-this))
  (1 (do-that))
  (2 (do-something-else)))

Теперь должно быть легко написать макрос, который расширяет (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)) в форму CASE ...

4 голосов
/ 30 ноября 2011

Вам не нужно eval здесь, и нет необходимости хранить выражения в списке.

Правильная реализация следующая:

(defmacro nth-expr (n &rest expressions)
    `(case ,n
        ,@(loop for e in expressions
                for n from 1
                collect
                `((,n) ,e))))

Ваш пример расширяется как:

(CASE N ((1) (/ 1 0)) ((2) (+ 1 2)) ((3) (/ 1 0)))
1 голос
/ 30 ноября 2011

Вы получили qoute ,expressions, например:

(defmacro nth-expr (n &rest expressions)
  (let ((i (gensym))
        (expr (gensym)))
    `(do ((,i 1 (1+ ,i))
          (,expr ',expressions (cdr ,expr)))
         ((= ,i ,n) (car ,expr)))))

В противном случае вы получите следующее - список expressions вставляется как:

CL-USER> (let ((n 2))
           (macroexpand-1 '(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))))
(DO ((#:G864 1 (1+ #:G864))
     (#:G865 ((/ 1 0) (+ 1 2) (/ 1 0)) (CDR #:G865)))
    ((= #:G864 N) (CAR #:G865)))
0 голосов
/ 30 ноября 2011

Ошибка в вашем втором примере заключается в том, что EVAL не может видеть ваши лексически связанные переменные N, X и Y.

Из CLHS для EVAL:

Оценивает форму в текущей динамической среде и нулевой лексической среде .

Это будет работать, есливы объявили X и Y специальными:

(let ((n 3) (x "win") (y "lose"))
  (declare (special x y))
  (nth-expr n (princ x) (princ x) (princ y) (princ x)))

, но это все же не так хорошо, как решение CASE, предложенное SK-logic .

...