2.9 可变性

有些 Lisp 对象永远不应该被修改。例如,Lisp 表达式 "aaa" 会生成一个字符串,但你不应该修改它的内容。还有一些对象本身就无法被修改;例如,虽然你可以通过计算得到一个新的数字,但 Lisp 不提供任何操作来修改一个已存在数字的值。

另一些 Lisp 对象是 mutable可变的:通过带有副作用的破坏性操作来修改它们的值是安全的。例如,一个已存在的标记(marker)可以通过移动到其他位置来被修改

虽然数字永远不可变、所有标记都是可变的,但某些类型既有可变成员,也有不可变成员。这些类型包括:点对(cons)、向量和字符串。例如:尽管 "cons"(symbol-name 'cons) 都生成不应该被修改的字符串,但 (copy-sequence "cons")(make-string 3 ?a) 都会生成可变字符串,后续可以通过 aset 来修改。

如果一个可变对象成为了被 eval 求值的表达式的一部分,它就不再是可变的。示例:

(let* ((x (list 0.5))
       (y (eval (list 'quote x))))
  (setcar x 1.5) ;; 程序不应该这么做
  y)

虽然列表 (0.5) 在创建时是可变的,但因为它被传给了 eval,就不应该再用 setcar 修改。反向情况不会发生:本就不应该被修改的对象,之后永远不会变成可变对象。

如果程序试图修改不应该被修改的对象,其行为是未定义的:Lisp 解释器可能会抛出错误,也可能崩溃,或以其他不可预测的方式运行。3

当相似的常量作为程序的一部分出现时,Lisp 解释器可能会为了节省时间或空间,复用已有的常量或其组成部分。例如:(eq "abc" "abc") 在解释器只创建一个字符串字面量 "abc" 实例时返回 t,创建两个实例时返回 nil。编写 Lisp 程序时,应保证无论是否启用这类优化,程序都能正常运行。


Footnotes

(3)

这是 Common Lisp、C 等语言对常量规定的行为,这与 JavaScript、Python 等语言不同 —— 后者要求解释器在程序试图修改不可变对象时必须抛出错误。理想情况下,Emacs Lisp 解释器未来会向后者方向演进。

emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike