Я написал макрос, чтобы сделать несколько вложенных циклов. Я понимаю, что для этого есть другие возможности, но я пытаюсь научиться писать макросы, и это казалось хорошим вариантом использования. Он тоже работает (вид):Правильный переход к макросу в Common Lisp
(defmacro dotimes-nested (nlist fn)
(let ((index-symbs nil)
(nlist (second nlist))) ;remove quote from the beginning of nlist
(labels
((rec (nlist)
(cond ((null nlist) nil)
((null (cdr nlist))
(let ((g (gensym)))
(push g index-symbs)
`(dotimes (,g ,(car nlist) ,g)
(funcall ,fn ,@(reverse index-symbs)))))
(t (let ((h (gensym)))
(push h index-symbs)
`(dotimes (,h ,(car nlist) ,h)
,(rec (cdr nlist))))))))
(rec nlist))))
погонных:
(macroexpand-1 '(dotimes-nested '(2 3 5)
#'(lambda (x y z) (format t "~A, ~A, ~A~%" x y z))))
выходы:
(DOTIMES (#:G731 2 #:G731)
(DOTIMES (#:G732 3 #:G732)
(DOTIMES (#:G733 5 #:G733)
(FUNCALL #'(LAMBDA (X Y Z) (FORMAT T "~A, ~A, ~A~%" X Y Z)) #:G731 #:G732
#:G733))))
и вызов макроса так:
(dotimes-nested '(2 3 5)
#'(lambda (x y z) (format t "~A, ~A, ~A~%" x y z)))
возвращается правильно, что Я ожидаю:
0, 0, 0
0, 0, 1
0, 0, 2
0, 0, 3
0, 0, 4
0, 1, 0
0, 1, 1
0, 1, 2
0, 1, 3
0, 1, 4
0, 2, 0
0, 2, 1
0, 2, 2
0, 2, 3
0, 2, 4
1, 0, 0
1, 0, 1
1, 0, 2
1, 0, 3
1, 0, 4
1, 1, 0
1, 1, 1
1, 1, 2
1, 1, 3
1, 1, 4
1, 2, 0
1, 2, 1
1, 2, 2
1, 2, 3
1, 2, 4
2
Однако, если я называю это так:
(let ((dims '(3 4)))
(dotimes-nested dims
#'(lambda (x y) (format t "~A, ~A~%" x y))))
Я получаю сообщение об ошибке:
; in: LET ((DIMS '(3 4)))
; (DOTIMES-NESTED DIMS #'(LAMBDA (X Y) (FORMAT T "~A, ~A~%" X Y)))
;
; caught ERROR:
; during macroexpansion of (DOTIMES-NESTED DIMS #'(LAMBDA # #)). Use
; *BREAK-ON-SIGNALS* to intercept.
;
; The value DIMS is not of type LIST.
; (LET ((DIMS '(3 4)))
; (DOTIMES-NESTED DIMS #'(LAMBDA (X Y) (FORMAT T "~A, ~A~%" X Y))))
;
; caught STYLE-WARNING:
; The variable DIMS is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 1 STYLE-WARNING condition
Как передать в переменную, которая не интерпретируется как символ но как это ценность? Должен ли я оценивать его в макросе, используя? Но я не могу понять, как это сделать. Также как будут работать обе ситуации: a: вызов макроса с литералом '(3 4) и b: передача символа, связанного с списком' (3 4)? Мне нужны отдельные макросы для каждого?
И наконец, у меня есть вторичный вопрос. Когда я передаю литерал для nlist, я должен использовать (second nlist)
, чтобы получить значения списка. Я понимаю, это потому, что '(3 4)
получает расширение до (quote (3 4))
перед отправкой на макрос. Это правильное предположение? И если да, то является ли эта общая практика, то есть использовать второе значение литерального списка, переданного макросу?