On Lisp
中的这段话确实令人困惑——不清楚返回引用的列表(如'(oh my)
)如何实际改变函数在未来的行为:下次调用时,返回的列表不会在函数中从头开始再次生成吗?
如果我们定义惊呼,使其返回值包含一个报价单,
(defun exclaim (expression) (append expression ’(oh my)))
随后对返回值进行任何破坏性修改
(exclaim ’(lions and tigers and bears)) -> (LIONS AND TIGERS AND BEARS OH MY) (nconc * ’(goodness)) -> (LIONS AND TIGERS AND BEARS OH MY GOODNESS)
可以更改功能中的列表:
(exclaim ’(fixnums and bignums and floats)) -> (FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS)
为了对这些问题进行有力的证明,应该写下:
(defun exclaim (expression) (append expression (list ’oh ’my)))
对exclaim
的最后一次调用到底是如何将单词goodness
添加到结果中的?该函数没有引用任何外部变量,那么对nconc
的单独调用实际上是如何改变exclaim
函数的工作方式的呢?
a)修改literal列表的效果在Common Lisp标准中是未定义的。你在这里看到的例子是一种可能的行为。
(1 2 3 4)
是一个文字列表。但是像(list 1 2 3 4)
中那样对LIST
的调用在运行时返回一个新保存的列表。
b) 列表是函数代码中的文字数据。每次调用都将返回该数据对象。如果你想在每次通话中提供一个新的列表,那么你需要使用list或COPY-list之类的东西。
c) 由于返回的列表始终是相同的文字数据对象,因此修改它可能会产生如上所述的效果。还可以想象,如果代码及其对象被分配在只读内存中,就会发生错误。然后修改列表将尝试写入只读内存。
d) 在处理源代码中的文字列表数据时,需要记住的一件事是:Lisp编译器可以自由地优化存储。如果一个列表恰好在源代码中多次出现,则允许编译器检测到这一点,并且只创建一个列表。所有不同的地方都会指向这个列表。因此,修改列表会产生这样的效果,即这些更改可能在几个地方可见。
这也可能发生在其他文字数据对象(如数组/向量)中。
如果您的数据结构是代码的一部分,则返回此内部数据结构,修改此数据结构,然后尝试修改代码。
还要注意,Lisp可以由解释器执行。解释器通常在Lisp源结构上工作——代码不是机器代码,而是将Lisp代码解释为Lisp数据。在这里,您可以在运行时修改源代码,而不仅仅是源代码中嵌入的数据。