我在使用以下宏时遇到问题:
(defmacro gather-params (&rest body)
"Return plist of params"
`(concatenate 'list
(map 'list
#'(lambda (plist)
(if (typep (first plist) 'keyword)
(cons 'list plist)
plist))
',body)))
在宏中,我无法通过在它前面添加逗号来使 plist 进行评估,例如: ,plist 当我这样做时,编译器会抱怨变量 ,plist 不存在。
关于宏中的范围,我有什么不明白的地方吗?
当前结果:
input: (gather-params (:mykey (+ 1 1)) (list 1 2 3))
result: ((LIST :MYKEY (+ 1 1)) (LIST 1 2 3))
期望的结果:
input: (gather-params (:mykey (+ 1 1)) (list 1 2 3))
result: ((LIST :MYKEY 2) (LIST 1 2 3))
回答1
(concatenate 'list '(1 2 3))
只是 (copy-list '(1 2 3))
-> 两者都评估为 (1 2 3)
。
宏生成代码。它有一个反引号列表。这个反引号列表是在宏扩展时计算的 -> 当宏扩展发生时,例如在编译期间。
生成的代码有一个函数,其参数名为 plist
。该函数在运行时执行。它在宏扩展期间不存在。因此 plist
在宏扩展期间是没有变量的。因此,您无法在宏展开期间计算 plist
的 value,因为那时该变量不存在。
如果你想评估代码,那么 Common Lisp 有函数 eval
。例如,可以在运行时调用 eval
。
CL-USER 31 > (defmacro gather-params (&rest body)
"Return plist of params"
`(map 'list
#'(lambda (plist)
(if (typep (first plist) 'keyword)
(list 'list
(first plist)
(eval (second plist)))
plist))
',body))
GATHER-PARAMS
CL-USER 32 > (gather-params (:mykey (+ 1 1)) (list 1 2 3))
((LIST :MYKEY 2) (LIST 1 2 3))
看看这个例子,用不同的方法:
CL-USER 42 > (defmacro gather-params (&rest body)
"Return plist of params"
`(list ,@(mapcar
(lambda (plist)
(if (typep (first plist) 'keyword)
(list 'list
''list
(first plist)
(second plist))
plist))
body)))
GATHER-PARAMS
CL-USER 43 > (let ((foo 1))
(gather-params (:mykey (+ foo foo)) (list 1 2 3)))
((LIST :MYKEY 2) (1 2 3))