2.9 可变性
一些 Lisp 对象永远不应该改变其内容。例如,Lisp 表达式"aaa"
生成一个字符串,但您不应更改其内容。有些对象是不可变的;例如,尽管您可以通过计算 1 来创建一个新数字,但 Lisp 不提供更改现有数字值的操作。
其他 Lisp 对象是可变的:通过具有副作用的破坏性操作来更改它们的值是安全且被允许的。例如,可以通过将标记移动到其他位置来更改现有标记。
数字永远是不可变的,并且所有标记都是可变的。此外,某些类型具有若干成员,其中一些成员是可变的,而另一些则不是。这些类型包括点对、向量和字符串。例如,虽然"cons"
和(symbol-name 'cons)
都生成不可变的字符串,但(copy-sequence "cons")
和 (make-string 3 ?a)
都生成可以通过 aset 改变的可变字符串。
如果可变对象是被求值的表达式的一部分,那它就不再是可变的。例如:
尽管该列表(0.5)
在创建时是可变的,但它不应通过 setcar
更改,因为它已提供给eval
用于求值。 相反的情况则不会发生:不可变的对象在此之后永远不会变得可变。
如果程序试图更改不可变对象,则结果行为是未定义的:Lisp 解释器可能会抛出错误信号,或者它可能会崩溃或出现其他方式无法预测的反应。2
当类似的常量作为程序的一部分出现时,Lisp 解释器可能会通过重用现有的常量或其组件来节省时间或空间。例如,在例子(eq "abc" "abc")
中,如果解释器只创建一个字符串常量的实例"abc"
返回 t
,如果它创建两个实例,则返回nil
。应该编写这样的 Lisp 程序,以便无论是否使用这种优化,它们都可以工作。
脚注
(2)
这是为 Common Lisp 和 C 等语言指定的常量行为,这与 JavaScript 和 Python 等语言不同,如果程序试图更改不可变对象,则需要解释器来发出错误信号。理想情况下,Emacs Lisp 解释器将会在后续版本中覆盖这一问题。
最后更新于
这有帮助吗?