我寫了一個宏來做多個嵌套循環。我知道還有其他設施可以做到這一點,但我正在努力學習如何編寫宏,這看起來是一個很好的用例。它的工作原理太(種):在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))
。這是一個正確的假設嗎?如果是這樣,這是一般的做法,即 - 使用傳遞給宏的文字列表的第二個值?