Strange Lisp Quoting场景 - Graham的On Lisp,第37页

时间:2010-10-23 08:28:34

标签: lisp common-lisp literals quoting on-lisp

我正在通过格雷厄姆的书“On Lisp”工作,并且无法理解第37页的以下示例:

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

(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)

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)))

有谁知道这里发生了什么?这严重妨碍了我引用的心理模型。

3 个答案:

答案 0 :(得分:7)

您的引用心理模型可能存在缺陷的观察结果非常好 - 尽管根据心理模型的不同,它可能适用也可能不适用。

首先,请记住程序执行有各种阶段。 Lisp环境必须首先读取程序文本为数据结构(列表,符号和各种文字数据,如字符串和数字)。接下来,它可能会或可能不会编译这些数据结构为机器代码或某种中间格式。最后,生成的代码评估(在机器代码的情况下,当然,这可能只是意味着跳转到适当的地址)。

让我们暂时把编译问题放在一边,重点关注阅读和评估阶段,假设(为简单起见)评估者的输入是读者阅读的数据结构列表。

考虑表单(QUOTE x),其中 x 是对象的一些文本表示。这可能是(QUOTE ABC)中的符号文字,(QUOTE (A B C))中的列表文字,(QUOTE "abc")中的字符串文字或任何其他类型的文字。在阅读阶段,读者将把表格读作一个列表(称之为 form1 ),其第一个元素是符号QUOTE,其第二个元素是对象 x'< / em>其文本表示为 x 。请注意,我特别说对象 x'存储在表示表达式的列表中,即在某种意义上,它存储为的一部分代码本身

现在轮到评估员了。评估者的输入是 form1 ,这是一个列表。所以它查看了 form1 的第一个元素,并确定它是符号QUOTE它作为评估结果返回列表的第二个元素< / strong>即可。这是至关重要的一点。评估者返回要评估的列表的第二个元素,这是读者在第一个执行阶段(编译之前)读入的内容。 这就是它所做的全部。它没有什么神奇之处,它非常简单,而且很明显,没有创建新对象而且没有复制现有对象。

因此,无论何时修改“引用列表”,您都要修改代码本身。自修改代码是一件非常令人困惑的事情,在这种情况下,行为实际上是未定义的(因为ANSI Common Lisp允许实现将代码放入只读内存中)。

当然,上述仅仅是一种心理模型。实现可以以各种方式自由地实现模型,事实上,我知道没有Common Lisp的实现,就像我的解释一样,根本没有编译。不过,这是基本的想法。

答案 1 :(得分:6)

nconc是一种破坏性的操作,通过改变它的尾部来改变它的第一个参数。在这种情况下,它意味着常量列表'(oh my)获得新的尾部。

希望能让这个更清楚。它有点像这样:

; Hidden variable inside exclaim
oh_my = oh → my → nil

(exclaim '(lions and tigers and bears)) =
    lions → and → tigers → and → bears → oh_my

(nconc * '(goodness)) destructively appends goodness to the last result:
    lions → and → tigers → and → bears → oh → my → goodness → nil
so now, oh_my = oh → my → goodness → nil

'(oh my)替换(list 'oh 'my)修复了这个问题,因为所有人都不再共享常量。每次调用exclaim都会生成一个新列表(list函数的用途就是创建全新的列表。)

答案 2 :(得分:6)

Common Lisp。

记住:

'(1 2 3 4)

以上是文字列表常量数据

(list 1 2 3 4)

LIST是一个函数,当调用返回新的新列表时,其参数作为列表元素。

避免修改文字列表。效果不规范。想象一下,Lisp将所有常量数据编译成只写存储区。想象一个Lisp,它采用常量列表并在函数之间共享它们。

(defun a () '(1 2 3)

(defun b () '(1 2 3))

Lisp编译器可以创建一个由两个函数共享的列表。

如果修改函数a返回的列表

  • 可能不会更改
  • 可能会更改
  • 可能是错误
  • 它也可能会更改功能b
  • 返回的列表

实施可以自由地做他们喜欢的事情。这留下了优化空间。