10

This passage from On Lisp is genuinely confusing -- it is not clear how returning a quoted list such as '(oh my) can actually alter how the function behaves in the future: won't the returned list be generated again in the function from scratch, the next time it is called?

If we define exclaim so that its return value incorporates a quoted list,

(defun exclaim (expression) 
  (append expression ’(oh my)))

Then any later destructive modification of the return value

(exclaim ’(lions and tigers and bears)) 
->  (LIONS AND TIGERS AND BEARS OH MY)
(nconc * ’(goodness))
->  (LIONS AND TIGERS AND BEARS OH MY GOODNESS)

could alter the list within the function:

(exclaim ’(fixnums and bignums and floats)) 
->  (FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS)

To make exclaim proof against such problems, it should be written:

(defun exclaim (expression)
  (append expression (list ’oh ’my)))

How exactly is that last call to exclaim adding the word goodness to the result? The function is not referencing any outside variable so how did the separate call to nconc actually alter how the exclaim function works?

4

1 回答 1

11

a) 修改文字列表的效果在 Common Lisp 标准中是未定义的。您在这里看到的示例是一种可能的行为。

(1 2 3 4)是一个文字列表。但是对LISTlike in的调用会(list 1 2 3 4)在运行时返回一个新的 consed 列表。

b) 列表是函数代码中的文字数据。每次调用都将准确返回此数据对象。如果你想在每次调用时提供一个新列表,那么你需要使用 LIST 或 COPY-LIST 之类的东西。

c) 由于返回的列表始终是相同的文字数据对象,因此修改它可以产生所描述的这种效果。还可以想象,如果代码及其对象被分配在只读存储器中,则会发生错误。然后修改列表将尝试写入只读内存。

d) 在源代码中处理文字列表数据时要记住的一件事是:Lisp 编译器可以自由地优化存储。如果一个列表恰好在源代码中出现多次,则允许编译器检测到这一点并只创建一个列表。然后所有不同的地方都会指向这个列表。因此,修改列表会产生这样的效果,即这些更改可以在多个地方可见。

这也可能发生在其他文字数据对象上,如数组/向量。

如果你的数据结构是代码的一部分,你返回这个内部数据结构,你修改这个数据结构——然后你尝试修改你的代码。

还要注意 Lisp 可以由解释器执行。解释器通常在 Lisp 源结构上工作——代码不是机器代码,而是将 Lisp 代码解释为 Lisp 数据。在这里,您也许可以在运行时修改源代码,而不仅仅是源代码中嵌入的数据。

于 2014-04-11T02:27:48.523 回答