关注Practical Common Lisp,我们正在寻找一个简单的自动化单元测试框架。我们正在尝试编写一个宏来使用:
(check (= (+ 1 2) 3) (= (- 1 4) 9))
这应该扩展为使用先前定义的函数report-result
的东西。建议的实施是:
(defmacro check (&body forms)
`(progn
,@(loop for f in forms collect `(report-result ,f ',f))))
然而,这种扩展对我来说似乎是程序性的。我想用loop
替换mapcar
以扩展为类似的内容:
(mapcar #'(lambda (form) (report-result form 'form)) (list form-1 ... form-n))
但是,我显然缺乏宏观写作技巧。有人能想出一个这样的宏吗?
如果相关,则为report-result
:
(defun report-result (result form)
(format t "~:[FAIL~;pass~] ... ~a~%" result form))
答案 0 :(得分:3)
确实相当简单:您只需将collect
表达式放入mapcar
的正文中:
(defmacro check (&body forms)
`(progn
,@(mapcar #'(lambda (form)
`(report-result ,form ',form))
forms)))
你真的不需要知道关于"宏-y"为了进行你想要的替换,这就是你正在进行的替换,这只是用一些其他等价的表达式替换loop
:它在宏上下文中也可以像在外面一样工作。 / p>
如果您希望扩展为mapcar
,但没有真正的理由这样做,因为列表的大小在编译时是已知的。这是什么样子:
(defmacro check (&body forms)
`(let ((results (list ,@(mapcar #'(lambda (form)
`(list ,form ',form))
forms))))
(mapcar #'(lambda (result)
(report-result (car result) (cadr result)))
results)))
扩展如此
> (macroexpand-1 '(check (+ 1 2) (* 2 3)))
(let ((results (list (list (+ 1 2) '(+ 1 2))
(list (* 2 3) '(* 2 3)))))
(mapcar #'(lambda (result) (report-result (car result) (cadr result)))
results))
你可以看到的是相当尴尬:宏已经有像(+ 1 2)
这样的形式,但是为了将它们保存到运行时以便mapcar lambda看,你必须发出输入形式两次。而且你必须制作整个列表来映射,而不是仅仅生成一个列表已经完成"首先。此外,这会产生一个列表作为输出,并且需要同时在内存中包含所有输入和输出:原始宏progn
一次生成一个输入和输出,并在完成后丢弃它们。