我的第一个Lisp宏;漏了吗?
我一直在研究"实用通用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发出失败信号)。此代码来自的特定上下文适用于配置文件的手写解析器,该语法具有完全拼凑的语法,即使用解析器生成器编写适当的解析器比手动滚动要费力。