8

这是一个一直让我好奇的疑问。

为什么在我们修改使用 Common Lisp 宏的定义时还需要重新编译它(当然,除了重新编译修改后的 Common Lisp 宏)?

提前致谢。

4

3 回答 3

11

Common Lisp 宏是代码替换,它们发生在“宏扩展时间”。对于解释代码,这可能是“运行时”。对于已编译的代码,它几乎总是在“编译时”。

一旦代码被替换,在生成的编译代码中就没有对宏的引用(如果宏的用法已经保存到文件中,磁盘上的原始源仍然有这个)。

虽然在一个 lisp 系统中会有一些便利,它可以保存所有“预宏扩展”函数体并跟踪在哪里使用了哪些宏,并自动重新编译任何使用给定宏的东西(可能是递归的),但剩下的就是超出标准。

通常,我在开发的早期就编写了我的实用程序宏,所以对我来说,没有太多需要拥有这个功能,总的来说,没有它我比有它更快乐(它减少了运行图像的大小有点,不需要跟踪所有这些)。

于 2013-06-07T11:35:07.837 回答
9

请注意,这可能是必要的。

原因是编译实现中的宏仅在编译时扩展一次,并且宏本身不存在于生成的代码中。

更清楚地考虑

(defmacro badsquare (x)
  `(* ,x ,x))

(defun f (x)
  (badsquare (+ x 3))

当编译器分析f代码时会将其扩展为

(defun f (x)
  (* (+ x 3) (+ x 3)))

并且对badsquare宏的引用不再一定存在,因此如果您将宏重新定义为其他内容,除非您也重新编译它badsquare,否则这将不起作用。f

于 2013-06-07T11:25:08.527 回答
3

仅在以下情况下才需要重新编译已更改的宏的用途:

  • 旧的宏有一个错误。宏有错误意味着它会生成错误的代码。坏代码必须由固定宏重新生成。

  • 旧的宏生成了糟糕的代码。我们想用最新最好的宏重新编译代码,生成最好的代码。

  • 对旧宏的运行时支持正在消失。旧的宏生成的代码调用包也提供的特殊运行时支持函数。其中一些功能将在新版本的宏包中消失,或者正在以向后不兼容的方式进行更改。因此,当安装新功能时,现有的编译代码将无法正常工作。推论:在维护具有运行时支持的宏包时,请记住可能不会重新编译的代码,并制定维护兼容性和强制淘汰的计划。

  • 一致性。您已经多次修改了多个宏,但您只根据宏重新编译了一些函数或源文件,而忽略了其他。因此,您正在测试一个“弗兰肯斯坦的怪物”图像,其中包含无法再从源代码重建的宏扩展。您正在进行的任何测试仅对该图像有效。当事情破裂时,可能只是因为不一致,所以你浪费时间去追逐不存在的错误。反之亦然:某些东西可以正常工作,但这只是由于旧代码和新代码的结合。您希望从头开始重新构建所有内容,以便为测试软件或打包发布具有明确定义的基线。

  • 界面变化。宏的语法已更改,因此必须更新代码以使用新宏。虽然可以继续使用用旧宏扩展的旧二进制代码,但您会遇到运行二进制文件的情况,这些二进制文件相对于更新的源代码是陈旧的(让我们回到之前的一致性点)。

如果这些条件都不适用,那么我们可以让自己懒惰地重新编译依赖于宏的代码。

于 2013-06-07T22:05:21.497 回答