这个宏有优势吗?

tes*_*lcl 8 lisp macros common-lisp practical-common-lisp

我正在阅读Peter Seibel的Practical Common Lisp.在第9章中,他正在通过创建单元测试框架来引导读者,并且他包含以下宏来确定列表是否仅由真实表达式组成:

(defmacro combine-results (&body forms)
  (let ((result (gensym)))
    `(let ((,result t))
       ,@(loop for form in forms collect `(unless ,form (setf ,result nil)))
       ,result)))
Run Code Online (Sandbox Code Playgroud)

我不清楚使用宏的优势在于什么 - 似乎以下内容会更清晰,并且对于动态值更有效:

(defun combine-results (&rest expressions)
  (let ((result t))
    (loop for expression in expressions do (unless expression (setf result nil)))
    result))
Run Code Online (Sandbox Code Playgroud)

宏的优点是,它在运行时对于在编译时扩展的任何调用更有效吗?或者它是一个范例的东西?或者这本书只是试图找借口在宏中练习不同的模式?

Rai*_*wig 9

在这种情况下,它可能无关紧要,但对于将来的版本,使用宏可能更有用.使用宏是否有意义?取决于用例:

使用功能

(combine-results (foo) (bar) (baz))
Run Code Online (Sandbox Code Playgroud)

请注意,在运行时Lisp看到这combine-results是一个函数.然后它评估参数.然后它combine-results使用结果值调用该函数.此评估规则已硬编码到Common Lisp中.

这意味着:在评估参数运行函数的代码.

使用宏

(combine-results (foo) (bar) (baz))
Run Code Online (Sandbox Code Playgroud)

由于Lisp看到它是一个宏,它在宏扩展时调用宏并生成代码.生成的代码是什么,完全取决于宏.这允许我们生成如下代码:

(prepare-an-environment

  (embed-it (foo))
  (embed-it (bar))
  (embed-it (baz))

  (do-post-processing))
Run Code Online (Sandbox Code Playgroud)

然后将执行此代码.因此,例如,您可以设置系统变量,提供错误处理程序,设置一些报告机制等.然后,每个单独的表单也可以嵌入到其他形式中.并运行功能后,我们可以做一些清理,报告等,prepare-an-environment以及embed-it将宏或特殊的运营商,这确保了一些代码运行之前,各地之后,我们所提供的嵌入形式.

我们将不得不代码执行之前,各地之后所提供的形式.这有用吗?有可能.它可能对更广泛的测试框架有用.

如果这听起来很熟悉,那么你会发现使用CLOS方法可以获得类似的代码结构(主要,前,后,周围).该测试将在主方法运行,而另一个代码将作为运行周围,的方法.

请注意,宏也可以打印(参见Hans23的评论),检查和/或改变提供的表格.


Kaz*_*Kaz 7

你的观察基本上是对的; 事实上你的功能可以是:

(defun combine-results (&rest expressions)
  (every #'identity expressions))  ;; i.e. all expressions are true?
Run Code Online (Sandbox Code Playgroud)

由于宏无条件地从左到右评估它的所有参数,并且T如果所有参数都为真,则产生它,它基本上只是内联优化,可以由函数完成.可以请求内联函数(declaim 'inline ...).而且,我们可以为函数编写一个编译器宏define-compiler-macro.有了这个宏,我们可以产生扩展,并将其作为我们可以apply和其他间接的功能.

计算函数内部结果的其他方法:

(not (position nil expressions))
(not (member nil expressions))
Run Code Online (Sandbox Code Playgroud)

该示例看起来像宏实践:制作gensym,并生成代码loop. 此外,宏是可能出现在单元测试框架中的起点.

  • @JoaoTavora如果宏被短路,它将是标准`和`宏的冗余实现! (2认同)