【发布时间】:2017-01-24 12:05:23
【问题描述】:
我编写了一个宏来执行多个嵌套循环。我知道还有其他工具可以做到这一点,但我正在尝试学习如何编写宏,这似乎是一个很好的用例。它也有效(有点):
(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))。这是一个正确的假设吗?如果是这样,这是否是一般做法,即 - 使用传递给宏的文字列表的第二个值?
【问题讨论】:
标签: macros common-lisp