@ mck, я понимаю, почему вы хотите использовать eval
сейчас. Но это очень грязное и медленное решение, как я уже упоминал в своем ответе на предыдущий вопрос. Классический на Лиспе говорит об этом eval
:
"Обычно не рекомендуется вызывать eval во время выполнения по двум причинам:
Это неэффективно: eval передается необработанный список, и любой должен скомпилировать его в
определить или оценить его в переводчике. В любом случае медленнее, чем компиляция
код заранее и просто вызывая его.
Он менее мощный, потому что выражение оценивается без лексического контекста.
Среди прочего, это означает, что вы не можете ссылаться на обычные
переменные, видимые вне вычисляемого выражения.
Обычно явный вызов eval подобен покупке чего-либо в сувенирном магазине в аэропорту.
Подождав до последнего момента, вы должны заплатить высокие цены за ограниченный
выбор второсортных товаров. "
В этом случае самое простое:
(defmacro testing-loop (var)
(let ((g (gensym)))
`(let ((,g ,var))
(if (consp ,g)
(loop for x from 0 to 5 collect x)
(loop for x from 0 to 5 and y in ,g collect y)))))
Я знаю, что вы хотите выделить общее loop for x from 0 to 5
(которое на самом деле не нужно во второй ветке в любом случае). Но loop
сам по себе макрос, который преобразуется во время компиляции в эффективный код низкого уровня. Таким образом, вызов loop
должен быть построен во время компиляции , используя значения, которые доступны во время компиляции. Вы не можете просто вставить туда (if)
, который предназначен для оценки во время выполнения.
Если вы действительно не хотите повторять loop for x from 0 to 5
, вы можете сделать что-то вроде:
(let ((a '(loop for x from 0 to 5)))
`(if (consp ,var)
(,@a collect x)
(,@a and y in ,var collect y)))
Это просто чтобы дать вам идею; если вы действительно делаете это, убедитесь, что gensym
!
Хороший урок, который можно извлечь из этого: когда вы пишете макросы, вы должны четко помнить, что происходит во время компиляции и что происходит во время выполнения. Макрос, который вы написали с помощью eval
, динамически компилирует макрос loop
, каждый раз, когда он запускается , на основе возвращаемого значения consp
. Вы действительно хотите скомпилировать 2 разных макроса loop
один раз и просто выбрать правильный во время выполнения.