# 2.9 可变性

一些 Lisp 对象永远不应该改变其内容。例如，Lisp 表达式`"aaa"`生成一个字符串，但您不应更改其内容。有些对象是不可变的；例如，尽管您可以通过计算 1 来创建一个新数字，但 Lisp 不提供更改现有数字值的操作。

其他 Lisp 对象是*可变的*：通过具有副作用的破坏性操作来更改它们的值是安全且被允许的。例如，可以通过将标记移动到其他位置来更改现有标记。

数字永远是不可变的，并且所有标记都是可变的。此外，某些类型具有若干成员，其中一些成员是可变的，而另一些则不是。这些类型包括点对、向量和字符串。例如，虽然`"cons"`和`(symbol-name 'cons)`都生成不可变的字符串，但`(copy-sequence "cons")`和 `(make-string 3 ?a)`都生成可以通过 aset 改变的可变字符串。

如果可变对象是被求值的表达式的一部分，那它就不再是可变的。例如：

```
(let* ((x (list 0.5))
       (y (eval (list 'quote x))))
  (setcar x 1.5) ;; 程序不应该做这样的事情
  y)
```

尽管该列表`(0.5)`在创建时是可变的，但它不应通过 `setcar` 更改，因为它已提供给`eval`用于求值。 相反的情况则不会发生：不可变的对象在此之后永远不会变得可变。

如果程序试图更改不可变对象，则结果行为是未定义的：Lisp 解释器可能会抛出错误信号，或者它可能会崩溃或出现其他方式无法预测的反应。[2](https://www.gnu.org/software/emacs/manual/html_node/elisp/Mutability.html#FOOT2)

当类似的常量作为程序的一部分出现时，Lisp 解释器可能会通过重用现有的常量或其组件来节省时间或空间。例如，在例子`(eq "abc" "abc")`中，如果解释器只创建一个字符串常量的实例`"abc"`返回 `t`，如果它创建两个实例，则返回`nil`。应该编写这样的 Lisp 程序，以便无论是否使用这种优化，它们都可以工作。

**脚注**

**(2)**

这是为 Common Lisp 和 C 等语言指定的常量行为，这与 JavaScript 和 Python 等语言不同，如果程序试图更改不可变对象，则需要解释器来发出错误信号。理想情况下，Emacs Lisp 解释器将会在后续版本中覆盖这一问题。
