我的第一个Lisp宏;漏了吗?

时间:2020-03-05 18:52:38  来源:igfitidea点击:

我一直在研究"实用通用Lisp",作为练习,我决定编写一个宏来确定一个数字是否是另一个数字的倍数:

(defmacro multp(值因子)(=(rem,value,factor)0))`

以便 :
(multp 40 10)
评估为true,而
(multp 40 13)
才不是

问题是此宏是否以某种方式泄漏?这也是"好" Lisp吗?我已经可以使用现有的功能/宏吗?

解决方案

回答

Siebel给出了可能的泄漏源的广泛清单(无论如何还是简单的情况),这里没有任何泄漏源。 "值"和"因数"都仅按顺序进行一次评估,"雷姆"没有任何副作用。

但是,这不是一个很好的Lisp,因为在这种情况下没有理由使用宏。一个功能

(defun multp (value factor)
  (zerop (rem value factor)))

对于所有实际目的都是相同的。 (请注意使用zerop。在这种情况下,我认为它使事情更清楚了,但是在需要突出显示的情况下,如果所测试的值不是零,则仍然有意义,(=。 .. 0)可能更好)

回答

宏在我看来不错。我不知道什么是泄漏宏,但是宏非常简单明了,不需要任何gensyms。就"好的" Lisp而言,我的经验法则是仅在函数无法使用时才使用宏,在这种情况下,可以使用函数代替宏。但是,如果此解决方案对我们有用,则没有理由不使用它。

回答

好吧,原则上,用户可以这样做:

(flet ((= (&rest args) nil))
  (multp 40 10))

它将评估为NIL ...,只是ANSI CL禁止重新绑定大多数标准符号(包括CL:=)是非法的,因此在这种情况下,立场是安全的。

当然,一般来说,我们应该同时注意参照不透明(从扩展宏的上下文中捕获标识符)和宏不卫生(将标识符泄漏到扩展代码中)。

回答

不,没有将宏的"词法闭包"中引入的符号释放到外部。

请注意,即使偶然发生的泄漏几乎总是这样,泄漏也不是一件坏事。对于我从事的一个项目,我发现类似于此的宏非常有用:

(defmacro ana-and (&rest forms)
  (loop for form in (reverse forms)
        for completion = form then `(let ((it ,form))
                                      (when it
                                       ,completion))
        finally (return completion)))

这使我能够"短路"需要按顺序完成的事情,并从顺序中的先前调用中继承参数(并且通过返回NIL发出失败信号)。此代码来自的特定上下文适用于配置文件的手写解析器,该语法具有完全拼凑的语法,即使用解析器生成器编写适当的解析器比手动滚动要费力。