【发布时间】:2013-06-03 15:00:18
【问题描述】:
这是一个一直让我好奇的疑问。
当我们修改使用 Common Lisp 宏的定义时,为什么还要重新编译它(当然,除了重新编译修改后的 Common Lisp 宏)?
提前致谢。
【问题讨论】:
标签: macros lisp common-lisp
这是一个一直让我好奇的疑问。
当我们修改使用 Common Lisp 宏的定义时,为什么还要重新编译它(当然,除了重新编译修改后的 Common Lisp 宏)?
提前致谢。
【问题讨论】:
标签: macros lisp common-lisp
Common Lisp 宏是代码替换,它们发生在“宏扩展时”。对于解释代码,这可能是“运行时”。对于已编译的代码,几乎总是在“编译时”。
一旦代码被替换,在生成的编译代码中就没有对宏的引用(磁盘上的原始源仍然有这个,如果宏的用法已经保存到一个文件中)。
虽然 lisp 系统会提供一些便利,它可以保存所有“预宏扩展”函数体并跟踪在何处使用了哪些宏,并自动重新编译使用给定宏的任何内容(可能是递归的),这被排除在标准之外。
通常,我在开发的早期就编写了我的实用程序宏,所以对我来说,没有太多需要拥有这个功能,总的来说,没有它我会比有它更快乐(它减少了运行图像大小相当大,不需要跟踪所有这些)。
【讨论】:
请注意,可能是必要的。
原因是编译实现中的宏在编译时只扩展一次,而宏本身并不存在于生成的代码中。
为了更清楚考虑
(defmacro badsquare (x)
`(* ,x ,x))
(defun f (x)
(badsquare (+ x 3))
当编译器分析f时,代码会将其展开为
(defun f (x)
(* (+ x 3) (+ x 3)))
并且对badsquare 宏的引用不再一定存在,因此如果您将badsquare 宏重新定义为其他内容,这将对f 没有影响,除非您还重新编译它。
【讨论】:
仅在以下情况下才需要重新编译已更改的宏的用途:
旧宏存在错误。宏有错误意味着它会生成错误的代码。坏代码必须由固定宏重新生成。
旧的宏生成了糟糕的代码。我们想用最新最好的宏重新编译代码,生成最好的代码。
对旧宏的运行时支持即将取消。旧的宏生成的代码调用包也提供的特殊运行时支持函数。其中一些功能将在新版本的宏包中消失,或者正在以向后不兼容的方式进行更改。因此,当安装新功能时,现有的编译代码将无法正常工作。 推论:在维护具有运行时支持的宏包时,请记住可能无法重新编译的代码,并制定维护兼容性和过时的计划。
一致性。您已经多次修改了多个宏,但您只根据宏重新编译了一些函数或源文件,而忽略了其他的。因此,您正在测试一个“弗兰肯斯坦的怪物”图像,其中包含无法再从源代码重建的宏扩展。您正在进行的任何测试仅对该图像有效。当事情破裂时,可能只是因为不一致,所以你浪费时间去追逐不存在的错误。反之亦然:某些东西可以正常工作,但这只是由于旧代码和新代码的结合。您希望从头开始重新构建所有内容,以便为测试软件或打包发布有一个明确定义的基线。
界面变化。宏的语法已更改,因此必须更新代码以使用新宏。虽然可以继续使用用旧宏扩展的旧二进制代码,但您会遇到运行二进制文件的情况,这些二进制文件相对于更新的源代码是陈旧的(让我们回到之前的一致性点)。
如果这些条件都不适用,那么我们可以让自己懒惰地重新编译依赖于宏的代码。
【讨论】: