我一直在研究Practical Common Lisp并且作为练习决定编写一个宏来确定一个数字是否是另一个数字的倍数:
(defmacro multp (value factor)
`(= (rem ,value ,factor) 0))
这样:
(multp 40 10)
评估为真
(multp 40 13)
问题是这个宏leak在某种程度上是这样吗?也是这个“好”的Lisp?是否已经存在我可以使用的现有功能/宏?
答案 0 :(得分:11)
Siebel给出了可能的泄漏源的广泛破坏(对于简单的情况),这里没有任何泄漏。 value
和factor
仅按顺序评估一次,rem
没有任何副作用。
这不是很好的Lisp,因为在这种情况下没有理由使用宏。功能
(defun multp (value factor)
(zerop (rem value factor)))
对于所有实际目的都是相同的。 (注意zerop
的使用。我认为在这种情况下它会让事情变得更清楚,但是在你需要强调的情况下,如果你正在测试的值是零,那么你正在测试的值可能仍然有意义{{{ 1}}可能会更好)
答案 1 :(得分:2)
你的宏看起来很好。我不知道漏洞是什么,但你的非常简单,不需要任何gensyms。至于这是“好”的Lisp,我的经验法则是仅在函数不能使用时才使用宏,在这种情况下,可以使用函数代替宏。但是,如果此解决方案适合您,则没有理由不使用它。
答案 2 :(得分:1)
原则上,用户可以这样做:
(flet ((= (&rest args) nil))
(multp 40 10))
将评估为NIL ...除了ANSI CL使得重新绑定大多数标准符号(包括CL:=)非法,因此在这种特殊情况下你是安全的。
当然,在generial中,你应该知道引用不透明(从宏扩展的上下文中捕获标识符)和宏不可靠(将标识符泄漏到扩展代码)。
答案 3 :(得分: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发出的失败)。这段代码来自的特定上下文是针对配置文件的手写解析器,该解析器具有拼凑在一起的足够语法,使用解析器生成器编写适当的解析器比手动操作更有效。