Nic*_*ger 13 lisp common-lisp quoting literals on-lisp
我正在通过格雷厄姆的书"On Lisp"工作,并且无法理解第37页的以下示例:
If we de?ne 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)))
有谁知道这里发生了什么?这严重妨碍了我引用的心理模型.
您的引用心理模型可能存在缺陷的观察结果非常好 - 尽管根据心理模型的不同,它可能适用也可能不适用.
首先,请记住程序执行有各种阶段.Lisp环境必须首先将程序文本读入数据结构(列表,符号和各种文字数据,如字符串和数字).接下来,它可能会也可能不会将这些数据结构编译成机器代码或某种中间格式.最后,评估结果代码(在机器代码的情况下,当然,这可能只是意味着跳转到适当的地址).
让我们暂时把编译问题放在一边,重点关注阅读和评估阶段,假设(为简单起见)评估者的输入是读者阅读的数据结构列表.
考虑一种形式(QUOTE x),其中x是对象的一些文本表示.这可以是符号文字,如(QUOTE ABC)列表文字,如(QUOTE (A B C))字符串文字(QUOTE "abc"),或任何其他类型的文字.在阅读阶段,读者将把表格读作一个列表(称之为form1),其第一个元素是符号QUOTE,第二个元素是文本表示为x的对象x'.请注意,我特别说对象x'存储在表示表达式的列表中,即在某种意义上,它存储为代码本身的一部分.
现在轮到评估员了.评估者的输入是form1,它是一个列表.因此,它查看了form1的第一个元素,并且在确定它是符号后QUOTE,它将作为评估结果返回列表的第二个元素.这是至关重要的一点.评估者返回要评估的列表的第二个元素,这是读者在第一个执行阶段(编译之前)读入的内容. 就是这样. 它没有什么神奇之处,它非常简单,而且很明显,没有创建新对象,也没有复制现有对象.
因此,无论何时修改"引用列表",您都要修改代码本身.自修改代码是一件非常令人困惑的事情,在这种情况下,行为实际上是未定义的(因为ANSI Common Lisp允许实现将代码放入只读内存中).
当然,以上仅仅是一种心理模型.实现可以以各种方式自由地实现模型,事实上,我知道没有Common Lisp的实现,就像我的解释一样,根本没有编译.不过,这是基本的想法.
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
Run Code Online (Sandbox Code Playgroud)
取而代之的'(oh my)是(list 'oh 'my)修复此问题,因为所有人都不再共享常量.每次调用都会exclaim生成一个新列表(该list函数在生活中的目的是创建全新的列表).
在Common Lisp中.
记得:
'(1 2 3 4)
Run Code Online (Sandbox Code Playgroud)
以上是文字列表.恒定数据.
(list 1 2 3 4)
Run Code Online (Sandbox Code Playgroud)
LIST是一个函数,当调用返回一个新的新列表,其参数作为列表元素.
避免修改文字列表.效果不规范.想象一下,Lisp将所有常量数据编译成只写存储区.想象一个Lisp,它采用常量列表并在函数之间共享它们.
(defun a () '(1 2 3)
(defun b () '(1 2 3))
Run Code Online (Sandbox Code Playgroud)
Lisp编译器可以创建一个由两个函数共享的列表.
如果修改函数a返回的列表
实现可以自由地做他们喜欢的事情.这留下了优化空间.