我正在尝试编写一个宏,该宏为数组结构数据结构(基于this代码段)定义了一些辅助函数。在该宏内,我定义了另一个宏,该宏有助于遍历struct中的所有插槽值。问题是我无法使双取消引号正常工作。这是代码:
(defmacro defcomponent (name-and-options &body slots)
(setf name-and-options (ensure-list name-and-options))
(let ((struct (first name-and-options))
(slot-names (iter (for s in slots)
(collecting
(ematch s
((or (and name (symbol)
(<> _ '*)
(<> _ nil))
(list* name _ (plist :type _ :read-only _)))
name))))))
`(progn (defstruct ,name-and-options
;; some task-specific stuff omitted here
)
(defmacro ,(symbolicate 'with- struct) (components &rest body)
`(loop
,@',(iter (for s in slot-names)
(appending `(for ,s across (,(symbolicate struct '- s) components))))
do ,@body)))))
因此,例如(defcomponent buzz x y)
宏扩展为
(PROGN
(DEFSTRUCT (BUZZ)
X Y) ;; details omitted
(DEFMACRO WITH-BUZZ (COMPONENTS &REST BODY)
`(LOOP ,@'(FOR X ACROSS (BUZZ-X COMPONENTS) FOR Y ACROSS (BUZZ-Y COMPONENTS))
DO ,@BODY))
这有点用,但是我想访问内部components
宏的with-buzz
参数,即类似这样的
(DEFMACRO WITH-BUZZ (COMPONENTS &REST BODY)
`(LOOP FOR X ACROSS (BUZZ-X ,COMPONENTS) FOR Y ACROSS (BUZZ-Y ,COMPONENTS)
DO ,@BODY))
我该如何实现?我尝试了,
和,@
的很多组合都没有用。
答案 0 :(得分:4)
有时,不使用反引号模式会有所帮助。然后,借助编译器可以更容易地理解范围问题,它会警告常见的变量范围问题。
作为一个稍微简化的练习,我们将编写一个函数,该函数生成代码。生成的代码是一个宏定义,它本身会生成代码。
(defun makeit (name slots)
(labels ((symbolicate (pattern &rest things)
(intern (apply #'format nil pattern things)))
(compute-for-clauses (slots)
(loop for s in slots
append (list ''for (list 'quote s)
''across (list 'list
(list 'quote
(symbolicate "~a-~a" name s))
'components)))))
(list 'progn
(list 'defmacro
(symbolicate "WITH-~a" name)
'(components &rest body)
(append '(list* 'loop)
(compute-for-clauses slots)
(list ''do 'body))))))
示例
CL-USER 51 > (pprint (makeit 'buzz '(x y)))
(PROGN
(DEFMACRO WITH-BUZZ (COMPONENTS &REST BODY)
(LIST* 'LOOP
'FOR
'X
'ACROSS
(LIST 'BUZZ-X COMPONENTS)
'FOR
'Y
'ACROSS
(LIST 'BUZZ-Y COMPONENTS)
'DO
BODY)))
CL-USER 52 > (eval *)
NIL
CL-USER 53 > (macroexpand-1 '(with-buzz a (+ 12) (+ 30)))
(LOOP FOR X ACROSS (BUZZ-X A) FOR Y ACROSS (BUZZ-Y A) DO (+ 12) (+ 30))
T
答案 1 :(得分:0)
好吧,我设法通过手动列表构建+ "EmailNextTimeDiscount"
而不是准引号来做到这一点,但是天哪,亲爱的母亲看起来太傻了。
eval
我很乐意接受任何其他惯用的答案来解决问题。