作者:拍友2502885255 | 来源:互联网 | 2023-06-17 13:19
我在 Emacs 中使用 Common Lisp 和 Slime。与使用突变相比,我有更多避免突变的经验。在 REPL 中,我可以执行以下操作:CL-USER> (defvar so-example
我在 Emacs 中使用 Common Lisp 和 Slime。与使用突变相比,我有更多避免突变的经验。
在 REPL 中,我可以执行以下操作:
CL-USER> (defvar so-example '())
SO-EXAMPLE
CL-USER> (push 7 so-example)
(7)
它有效。但是,如果我尝试:
CL-USER> (push 7 '())
我收到一条错误消息说:
未定义的函数:(SETF QUOTE)
好的。由于quote
是一个问题,我也尝试过:
CL-USER> (push 7 nil)
这也会引发错误消息:
NIL 是一个常量,因此不能设置。
为什么会发生这种情况?为什么说有道理?
对我来说,这很奇怪。
回答
修改文字常量会导致 Common Lisp 中的未定义行为;来自 HyperSpec:
如果文字对象(包括带引号的对象)被破坏性地修改,后果是不确定的。
这解释了观察到的错误消息(push 7 nil)
;将伴随相同的错误消息(push 7 ())
。
表达式(push 7 '())
等价于(setf '() (cons 7 '()))
。setf
期待一个地方,但(quote ())
不是一个地方。当setf
宏意识到错误时,它正在捕捉错误quote
不能产生一个可设置的地方。
请注意,根据文档,push
需要一个项目和一个位置(不是列表):“push
将项目添加到存储到位的列表中...... ”。也就是说,位置是对列表的引用,而不是列表本身。
一般来说,尝试改变这样的字面常量没有多大意义。考虑用数字来做这件事。试图通过修改数字 7 将 7 更改为 8 不是我们想要做的。相反,我们将建立一个绑定到7,和变异的结合。用空列表做同样的事情:
CL-USER> (defvar empty-list '())
SO-EXAMPLE
CL-USER> (setf empty-list (cons 7 empty-list))
(7)
这里建立了到空列表的绑定(empty-list
),然后绑定发生了变化,绑定empty-list
到通过 consing 7
to创建的新列表()
。这正是在第一个发布的示例中发生的事情。
CL-USER> (defvar so-example '())
SO-EXAMPLE
CL-USER> (push 7 so-example)
(7)
使用算术的类似示例可能是:
CL-USER> (defvar x 7)
X
CL-USER> (setf x (+ 1 x))
8
CL-USER> x
8
然而,你可能不会期望(setf 7 (+ 1 7))
做任何好事。
回答
宏 的文档PUSH
说:
push item place => new-place-value
位置是广义引用:变量、数组槽、结构槽、CLOS 对象槽等等。它们记录在此处:CLHS 5.1 Generalized Reference
广义是什么意思?在 Common Lisp 中,位置是一个超越变量或槽等简单引用的概念。它甚至是用户可扩展的 -> 可以定义新的场所类型。
NIL 作为一个地方
NIL
(相同()
)被记录为一个Constant Variable。
因此,这样的事情失败了:
(setf nil 10)
(push 10 nil)
正试图推10
到那个地方 NIL
。
CL-USER 1 > (macroexpand-1 '(push 10 nil))
(LET ((#:|new-value-1070| 10))
(LET* ((#:|Store-Var-1069| (CONS #:|new-value-1070| NIL)))
(SETQ NIL #:|Store-Var-1069|)))
有它:尝试设置NIL
(作为参考,这里是一个变量)值为 10。
既然NIL
被定义为常量变量,常量变量是不能改变的,所以不能把东西推到那个地方nil
。
因此NIL
不是一个有用的地方。
'NIL 作为一个地方
为什么会(push 10 '())
失败?这与(push 10 'nil)
和相同(push 10 (quote nil))
。
再次,push
期待一个地方。(quote ...)
不是一个定义的地方。
此处定义了默认定义的地点:CLHS 5.1.2 Kinds of Places。
这是有道理的,因为'()
没有引用任何东西。记住:地方是广义的参考。它应该是一个文字对象()
。
功能缺点
如果我们想向列表中添加一个新对象,那么我们使用cons
:
CL-USER 4 > (cons 10 nil)
(10)
CL-USER 5 > (cons 10 'nil)
(10)
一个必须使用结果列表:将它存储在某个地方,将它传递给一个函数,......