Существует множество основных проблем (например, какой код должен быть сгенерирован и когда) в уже слегка сложном макросе. Сначала вы можете подумать о более простых примерах макросов.
Но можно заставить ваш код работать, поэтому не все потеряно.
Давайте рассмотрим некоторые проблемы:
Как использовать макрос в коде
Вы хотите использовать свой макрос следующим образом:
(do-combinations (n '((1 2 3)
(10 20 30)
(100 200 300)))
(pprint n))
Но нет смысла цитировать вложенный список. Макрос генерирует код, вероятно, во время компиляции, и в то время список должен быть известен. Таким образом, вы не можете или не можете это оценить. Таким образом можно удалить цитату:
(do-combinations (n ((1 2 3)
(10 20 30)
(100 200 300)))
(pprint n))
Некоторые основы макросов
Теперь, когда вы пишете макрос, нужно понимать следующие основные вещи:
- макрос сгенерирует код. Вы должны знать, какой код должен генерировать ваш макрос. Запишите код и сравните его с тем, что делает ваш макрос.
- чтобы увидеть, что генерирует макрос, используйте
macroexpand
и macroexpand-1
. Используйте pprint
, чтобы красиво напечатать полученный код.
Давайте посмотрим на генерируемый код
Теперь давайте посмотрим на код, который генерирует ваш макрос:
CL-USER 145 > (pprint
(macroexpand-1 '(do-combinations (n ((1 2 3)
(10 20 30)
(100 200 300)))
(pprint n))))
(LET* ((LST
(MAPCAR #'(LAMBDA (X) `(LOOP FOR ,(GENSYM) IN (LIST ,@X) DO))
((1 2 3) (10 20 30) (100 200 300))))
(SYMBOLS (MAPCAR #'CADDR LST)))
(REDUCE #'(LAMBDA (X Y) `(,@Y ,X))
LST
:INITIAL-VALUE
`(LET ((N (LIST ,@SYMBOLS))) (PROGN (PPRINT N)))))
Вы можете видеть, что все это неправильно, поскольку генерируется много кода, который должен выполняться во время расширения макроса, а не во время выполнения! Это вовсе не генерация вложенных циклов.
Вы можете увидеть в своем макросе эту вторую строку:
`(let* ((lst (mapcar #'(lambda (x)
Это означает, что код будет сгенерирован. Но вы, вероятно, хотите запустить его в фазе расширения.
Лучшая версия
Вот версия с правильным генерированием кода:
(defmacro do-combinations ((var lists) &body body)
(let* ((lst (mapcar #'(lambda (x)
`(loop for ,(gensym) in (list ,@x) do))
lists))
(symbols (mapcar #'caddr lst)))
(reduce #'(lambda (x y) `(,@y ,x))
lst
:initial-value `(let ((,var (list ,@symbols)))
,@body))))
Посмотрим:
CL-USER 147 > (pprint
(macroexpand-1 '(do-combinations (n ((1 2 3)
(10 20 30)
(100 200 300)))
(pprint n))))
(LOOP FOR #:G424120 IN (LIST 100 200 300)
DO (LOOP FOR #:G424119 IN (LIST 10 20 30)
DO (LOOP FOR #:G424118 IN (LIST 1 2 3)
DO (LET ((N (LIST #:G424118 #:G424119 #:G424120)))
(PPRINT N)))))