utx*_*eee 10 macros common-lisp
在我完全理解如此强大的lisp宏的过程中,我想到了一个问题.我知道关于宏的一条黄金法则是"当一个函数完成工作时不要使用宏".然而阅读第9章 - 实践:构建单元测试框架 - 从实用Common Lisp I一书中介绍了下面的宏,其目的是消除测试用例表达式的重复,伴随着结果错误标记的风险.
;; Function defintion.
(defun report-result (result form)
(format t "~:[FAIL~;pass~] ... ~a~%" result form))
;; Macro Definition
(defmacro check (form)
`(report-result ,form ',form))
Run Code Online (Sandbox Code Playgroud)
好的,我理解它的目的,但我可以使用函数而不是宏来完成它,例如:
(setf unevaluated.form '(= 2 (+ 2 3)))
(defun my-func (unevaluated.form)
(report-result (eval unevaluated.form) unevaluated.form))
Run Code Online (Sandbox Code Playgroud)
Wil*_*ung 13
但如果它是一个宏,你可以做到:
(check (= 2 (+ 2 3)))
Run Code Online (Sandbox Code Playgroud)
使用功能,您必须:
(check '(= 2 (+ 2 3)))
Run Code Online (Sandbox Code Playgroud)
此外,使用宏(= 2 (+ 2 3))实际上是由编译器编译的,而对于函数,它由eval函数计算,不一定是相同的东西.
附加物:
是的,它只是评估功能.现在这意味着什么取决于实施.有些人可以解释它,其他人可以编译和执行它.但简单的问题是你不知道从一个系统到另一个系统.
其他人提到的空词汇环境也是一个大问题.
考虑:
(defun add3f (form)
(eval `(+ 3 ,form)))
(demacro add3m (form)
`(+ 3 ,form))
Run Code Online (Sandbox Code Playgroud)
然后观察:
[28]> (add3m (+ 2 3))
8
[29]> (add3f '(+ 2 3))
8
[30]> (let ((x 2)) (add3m (+ x 3)))
8
[31]> (let ((x 2)) (add3f '(+ x 3)))
*** - EVAL: variable X has no value
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead of X.
STORE-VALUE :R2 Input a new value for X.
ABORT :R3 Abort main loop
Break 1 [32]> :a
Run Code Online (Sandbox Code Playgroud)
对于大多数用例来说,这真的很糟糕.由于eval没有词汇环境,因此无法x从封闭中"看到" let.
更好的替换不是 with eval,它不会在所有情况下都按预期执行(例如,它无法访问词法环境),而且也是矫枉过正(请参见此处:https ://stackoverflow.com/ a/2571549/977052),但是使用匿名函数,如下所示:
(defun check (fn)
(report-result (funcall fn) (function-body fn)))
CL-USER> (check (lambda () (= 2 (+ 2 3))))
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这就是 Ruby 中完成此类事情的方式(在那里调用匿名函数procs)。
但是,正如您所看到的,它变得不太优雅(除非您添加语法糖),而且实际上存在一个更大的问题:Lisp 中没有function-body函数(尽管可能有非标准的方法来实现它)。总的来说,正如您所看到的,对于这个特定的任务,替代解决方案要糟糕得多,尽管在某些情况下这种方法可能有效。
不过,一般来说,如果您想对传递到宏的表达式的源代码执行某些操作(通常这是使用宏的主要原因),函数是不够的。