12.8 常量结构 (Constant Structure)

因为常量实际上是程序代码的一部分,所以我们也不应该修改他们,或者是不经意地写了自重写的代码。一个通过 quote 引用的列表是一个常量,所以一定要小心,不要修改被引用的列表的任何 cons。比如,如果我们用下面的代码,来测试一个符号是不是算术运算符:

  1. (defun arith-op (x)
  2. (member x '(+ - * /)))

如果被测试的符号是算术运算符,它的返回值将至少一个被引用列表的一部分。如果我们修改了其返回值,

  1. > (nconc (arith-op '*) '(as i t were))
  2. (* / AS IT WERE)

那么我就会修改 arith-op 函数中的一个列表,从而改变了这个函数的功能:

  1. > (arith-op 'as )
  2. (AS IT WERE)

写一个返回常量结构的函数,并不一定是错误的。但当你考虑使用一个破坏性的操作是否安全的时候,你必须考虑到这一点。

有几个其它方法来实现 arith-op,使其不返回被引用列表的部分。一般地,我们可以通过将其中的所有引用( quote ) 替换成 list 来确保安全,这使得它每次被调用都将返回一个新的列表:

  1. (defun arith-op (x)
  2. (member x (list '+ '- '* '/)))

这里,使用 list 是一种低效的解决方案,我们应该使用 find 来替代 member

  1. (defun arith-op (x)
  2. (find x '(+ - * /)))

这一节讨论的问题似乎只与列表有关,但实际上,这个问题存在于任何复杂的对象中:数组,字符串,结构,实例等。你不应该逐字地去修改程序的代码段。

即使你想写自修改程序,通过修改常量来实现并不是个好办法。编译器将常量编译成了代码,破坏性的操作可能修改它们的参数,但这些都是没有任何保证的事情。如果你想写自修改程序,正确的方法是使用闭包 (见 6.5 节)。