为什么我不能 (push 3 '()) 在 Common Lisp 的 REPL 中?

Ped*_*ino 2 common-lisp read-eval-print-loop

我在 Emacs 中使用 Common Lisp 和 Slime。与使用突变相比,我有更多避免突变的经验。

在 REPL 中,我可以执行以下操作:

CL-USER> (defvar so-example '())
SO-EXAMPLE

CL-USER> (push 7 so-example)
(7)
Run Code Online (Sandbox Code Playgroud)

它有效。但是,如果我尝试:

CL-USER> (push 7 '())
Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息说:

未定义的函数:(SETF QUOTE)

好的。由于quote是一个问题,我也尝试过:

CL-USER> (push 7 nil)
Run Code Online (Sandbox Code Playgroud)

这也会引发错误消息:

NIL 是一个常量,因此不能设置。

为什么会发生这种情况?为什么说有道理?

对我来说,这很奇怪。

ex *_*ilo 8

修改文字常量会导致 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)
Run Code Online (Sandbox Code Playgroud)

这里建立了到空列表的绑定empty-list),然后绑定发生了变化,绑定empty-list到通过 consing 7to创建的新列表()。这正是在第一个发布的示例中发生的事情。

CL-USER> (defvar so-example '())
SO-EXAMPLE
CL-USER> (push 7 so-example)
(7)
Run Code Online (Sandbox Code Playgroud)

使用算术的类似示例可能是:

CL-USER> (defvar x 7)
X
CL-USER> (setf x (+ 1 x))
8
CL-USER> x
8
Run Code Online (Sandbox Code Playgroud)

然而,你可能不会期望(setf 7 (+ 1 7))做任何好事。


Rai*_*wig 5

的文档PUSH说:

push item place => new-place-value
Run Code Online (Sandbox Code Playgroud)

位置广义引用:变量、数组槽、结构槽、CLOS 对象槽等等。它们记录在此处:CLHS 5.1 Generalized Reference

广义是什么意思?在 Common Lisp 中,位置是一个超越变量或槽等简单引用的概念。它甚至是用户可扩展的 -> 可以定义新的场所类型。

NIL 作为一个地方

NIL(相同())被记录为一个Constant Variable

因此,这样的事情失败了:

(setf nil 10)
Run Code Online (Sandbox Code Playgroud)

(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|)))
Run Code Online (Sandbox Code Playgroud)

有它:尝试设置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)
Run Code Online (Sandbox Code Playgroud)

一个必须使用结果列表:将它存储在某个地方,将它传递给一个函数,......