Common Lisp: Как построить список в макросе с условным объединением? - PullRequest
2 голосов
/ 09 февраля 2012

Предположим:

(defmacro testing (&optional var)
    `(list 'this 'is  
       ,@(when (consp var) `('a 'list))))

при вызове:

>(testing 2)
(THIS IS)

>(testing (list 1 2))
(THIS IS A LIST)

что я и хотел. Но теперь, когда я передаю параметр, представляющий собой список:

>(defparameter bla (list 1 2 3))
BLA
>(testing bla)
(THIS IS)

что, я полагаю, связано с тем, что макрос будет проверять (consp bla) где bla - символ, а не список? Как мне это предотвратить?

Спасибо

Ответы [ 2 ]

4 голосов
/ 09 февраля 2012

Вы можете сделать что-то вроде:

(defmacro testing (&optional var)
   `(if (consp ,var)
        '(this is a list)
        '(this is)))

Таким образом, var будет оцениваться во время выполнения (не во время компиляции). var появляется только один раз в расширении макроса, но если бы он появлялся более одного раза, вам пришлось бы использовать gensym.

РЕДАКТИРОВАТЬ: Если вы не хотите вводить '(this is) дважды, сделайте это:

(defmacro testing (&optional var)
  `(append '(this is) (when (consp ,var) '(a list))))

Не используйте eval, это медленно и совершенно не нужно. Подставив var в расширение макроса, он, естественно, будет оцениваться во время выполнения. Если вы используете eval, вы будете делать что-то вроде этого:

(eval (append '(list 'this 'is) (when (consp 'bla) '('a 'list))))

Каждый раз, когда выполняется 1018 *, он создает список, представляющий код, и компилирует его перед запуском. (Надеюсь, это не в цикле!) Если вы просто используете макрос, который генерирует простой код (без eval), он будет скомпилирован только один раз.

0 голосов
/ 09 февраля 2012

Проблема здесь в том, что выражение

,@(when (consp var) `('a 'list))))

оценивается во время компиляции, когда у вас есть только буквальные (неоцененные) значения аргументов В вашем случае: 2, (list 1 2) и bla.

Единственное решение, о котором я знаю, это использовать eval. Этот конкретный пример можно изменить следующим образом:

(defmacro testing (&optional var)
  `(eval (append '(list 'this 'is)  
                 (when (consp ',var)
                   '('a 'list))))

Но, я думаю, вы согласитесь, что это действительно ужасно. И это не сработает, если вы хотите использовать лексические переменные. Обычно есть способы переформулировать проблему, чтобы такие извращения не были нужны.

...