将循环重写为 mapcar
Rewrite loop as a mapcar
查看 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))))
但是,这种扩展对我来说似乎是程序性的。我想用 mapcar
替换 loop
以扩展为这样的内容:
(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))
这确实相当简单:您只需将 collect
表达式放入 mapcar
:
的正文中
(defmacro check (&body forms)
`(progn
,@(mapcar #'(lambda (form)
`(report-result ,form ',form))
forms)))
您真的不需要了解正在发生的 "macro-y" 事情,就可以进行您想要的替换,这只是用其他等效表达式替换 loop
: 它在宏观环境中和在外部环境中一样有效。
如果你想扩展到 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 查看,您必须发出输入表格两次。而且您必须生成整个列表以进行映射,而不仅仅是生成一个 "finished" 开头的列表。此外,这会生成一个列表作为输出,并且需要同时将所有输入和输出存储在内存中:带有 progn
的原始宏一次生成一个输入和输出,并在完成后丢弃它们。
查看 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))))
但是,这种扩展对我来说似乎是程序性的。我想用 mapcar
替换 loop
以扩展为这样的内容:
(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))
这确实相当简单:您只需将 collect
表达式放入 mapcar
:
(defmacro check (&body forms)
`(progn
,@(mapcar #'(lambda (form)
`(report-result ,form ',form))
forms)))
您真的不需要了解正在发生的 "macro-y" 事情,就可以进行您想要的替换,这只是用其他等效表达式替换 loop
: 它在宏观环境中和在外部环境中一样有效。
如果你想扩展到 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 查看,您必须发出输入表格两次。而且您必须生成整个列表以进行映射,而不仅仅是生成一个 "finished" 开头的列表。此外,这会生成一个列表作为输出,并且需要同时将所有输入和输出存储在内存中:带有 progn
的原始宏一次生成一个输入和输出,并在完成后丢弃它们。