Lisp:宏与函数

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)
  1. 这是唯一可能的,因为给定的宏太简单了吗?
  2. 此外,由于代码本身 - 如控制结构,功能等 - 相对于其对手而言,Lisp宏系统是如此强大 - 被表示为LIST吗?

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.


Vse*_*kin 4

更好的替换不是 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函数(尽管可能有非标准的方法来实现它)。总的来说,正如您所看到的,对于这个特定的任务,替代解决方案要糟糕得多,尽管在某些情况下这种方法可能有效。

不过,一般来说,如果您想对传递到宏的表达式的源代码执行某些操作(通常这是使用宏的主要原因),函数是不够的。