5

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

(defmacro multp (value factor)
`(= (rem ,value ,factor) 0))

所以 : (multp 40 10) 评估为 true 而 (multp 40 13) 不是

问题是这个宏是否以某种方式泄漏?这也是“好”的 Lisp 吗?是否已经有我可以使用的现有函数/宏?

4

4 回答 4

11

Siebel 对可能的泄漏源进行了广泛的概述(无论如何都是简单的情况),这里没有任何一个。value和都只factor按顺序评估一次,并且rem没有任何副作用。

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

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

对于所有实际目的都是相同的。(注意使用zerop. 我认为在这种情况下它使事情更清楚,但在您需要强调的情况下,如果您正在测试的值不是零,那么它可能仍然有意义,(= ... 0)可能会更好)

于 2008-09-13T02:21:26.400 回答
2

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

于 2008-09-13T02:21:44.840 回答
1

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

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

这将评估为 NIL ...除了 ANSI CL 使重新绑定大多数标准符号(包括 CL:=)是非法的,因此在这种特殊情况下你是安全的。

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

于 2008-09-17T10:28:16.570 回答
0

不,宏的“词法闭包”中引入的符号不会被释放到外部。

请注意,泄漏不一定是坏事,即使意外泄漏几乎总是如此。对于我从事的一个项目,我发现类似于此的宏很有用:

(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 表示失败)。此代码来自的特定上下文是用于配置文件的手写解析器,该配置文件具有足够拼凑的语法,使用解析器生成器编写适当的解析器比手动滚动更多工作。

于 2008-11-06T11:57:48.930 回答