为什么我不能在 Common Lisp 的 REPL 中 (push 3 '())?
Why I can't (push 3 '()) in Common Lisp's REPL?
我在 Emacs 中使用 Common Lisp 和 Slime。我对避免变异的经验比使用它更多。
在REPL中我可以做的:
CL-USER> (defvar so-example '())
SO-EXAMPLE
CL-USER> (push 7 so-example)
(7)
并且有效。但是,如果我尝试:
CL-USER> (push 7 '())
我收到一条错误消息:
Undefined function: (SETF QUOTE)
好的。由于quote
是一个问题,我也试过:
CL-USER> (push 7 nil)
这也会引发错误消息:
NIL is a constant and thus can't be set.
为什么会这样?为什么说得通?
对我来说,这很奇怪。
修改文字常量会导致 Common Lisp 中出现未定义的行为; from the HyperSpec:
The consequences are undefined if literal objects (including quoted objects) are destructively modified.
这解释了使用 (push 7 nil)
观察到的错误消息; (push 7 ())
.
会出现相同的错误消息
表达式(push 7 '())
等价于(setf '() (cons 7 '()))
。 setf
需要 place,但 (quote ())
不是一个地方。 setf
宏在意识到 quote
无法生成可设置的位置时正在捕获错误。
请注意 according to the documentation、push
需要 item 和 place(不是 list): "push
将项目添加到存储在适当位置的列表中...."。也就是说,地方 是对列表的引用,而不是列表本身。
一般来说,像这样尝试改变文字常量没有多大意义。考虑用数字来做这件事。试图通过修改数字 7 将 7 更改为 8 不是我们想要做的。相反,我们将建立一个 binding 到 7,并改变绑定。对空列表做同样的事情:
CL-USER> (defvar empty-list '())
SO-EXAMPLE
CL-USER> (setf empty-list (cons 7 empty-list))
(7)
此处建立了到空列表的绑定 (empty-list
),然后 绑定 发生变异,将 empty-list
绑定到创建的新列表将 7
转换为 ()
。这正是第一个发布的有效示例中发生的事情。
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
是一个宏,它为看起来不同的第二个参数做不同的事情。您可以通过查看 maroexpand-1
:
来检查它的作用
(macroexpand-1 '(push 5 binding))
; ==> (set1 binding (cons 5 binding))
(macroexpand-1 '(push 5 (car structure)))
; ==> (let* ((tmp structure))
; (rplaca tmp (cons 5 (car tmp))))
它如何知道要做什么是通过查看 setf
函数。例如。呼叫:
(get-setf-expansion 'binding)
; ==> ()
; ()
; (tmp)
; (setq binding tmp)
; binding
您可以定义自己的,例如。 a class 这样你就可以将 push
个元素添加到你创建的 class 的对象中,并让它做一些特别的事情。例如。您正在扩展 push
的语言功能,这样它也支持您制作的 class。
那么当我计算 (get-setf-expansion ''())
时会发生什么 你明白为什么 (macroexpand-1 '(push 3 '())
的扩展变成了:
(let* ((tmp (cons 3 '())))
(funcall #'(setf quote) tmp '()))
还有你异常错误消息的来源。
宏 PUSH
的文档说:
push item place => new-place-value
地方是广义引用:变量、数组槽、结构槽、CLOS对象槽等等。它们记录在此处:CLHS 5.1 Generalized Reference
广义是什么意思?在 Common Lisp 中,places 是一个超越变量或槽等简单引用的概念。它甚至是用户可扩展的 -> 可以定义新类型的 places.
NIL 作为一个地方
NIL
(与 ()
相同)记录为 常量变量.
因此这样的事情失败了:
(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)
必须使用结果列表:将其存储在某处,将其传递给函数,...
我在 Emacs 中使用 Common Lisp 和 Slime。我对避免变异的经验比使用它更多。
在REPL中我可以做的:
CL-USER> (defvar so-example '())
SO-EXAMPLE
CL-USER> (push 7 so-example)
(7)
并且有效。但是,如果我尝试:
CL-USER> (push 7 '())
我收到一条错误消息:
Undefined function: (SETF QUOTE)
好的。由于quote
是一个问题,我也试过:
CL-USER> (push 7 nil)
这也会引发错误消息:
NIL is a constant and thus can't be set.
为什么会这样?为什么说得通?
对我来说,这很奇怪。
修改文字常量会导致 Common Lisp 中出现未定义的行为; from the HyperSpec:
The consequences are undefined if literal objects (including quoted objects) are destructively modified.
这解释了使用 (push 7 nil)
观察到的错误消息; (push 7 ())
.
表达式(push 7 '())
等价于(setf '() (cons 7 '()))
。 setf
需要 place,但 (quote ())
不是一个地方。 setf
宏在意识到 quote
无法生成可设置的位置时正在捕获错误。
请注意 according to the documentation、push
需要 item 和 place(不是 list): "push
将项目添加到存储在适当位置的列表中...."。也就是说,地方 是对列表的引用,而不是列表本身。
一般来说,像这样尝试改变文字常量没有多大意义。考虑用数字来做这件事。试图通过修改数字 7 将 7 更改为 8 不是我们想要做的。相反,我们将建立一个 binding 到 7,并改变绑定。对空列表做同样的事情:
CL-USER> (defvar empty-list '())
SO-EXAMPLE
CL-USER> (setf empty-list (cons 7 empty-list))
(7)
此处建立了到空列表的绑定 (empty-list
),然后 绑定 发生变异,将 empty-list
绑定到创建的新列表将 7
转换为 ()
。这正是第一个发布的有效示例中发生的事情。
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
是一个宏,它为看起来不同的第二个参数做不同的事情。您可以通过查看 maroexpand-1
:
(macroexpand-1 '(push 5 binding))
; ==> (set1 binding (cons 5 binding))
(macroexpand-1 '(push 5 (car structure)))
; ==> (let* ((tmp structure))
; (rplaca tmp (cons 5 (car tmp))))
它如何知道要做什么是通过查看 setf
函数。例如。呼叫:
(get-setf-expansion 'binding)
; ==> ()
; ()
; (tmp)
; (setq binding tmp)
; binding
您可以定义自己的,例如。 a class 这样你就可以将 push
个元素添加到你创建的 class 的对象中,并让它做一些特别的事情。例如。您正在扩展 push
的语言功能,这样它也支持您制作的 class。
那么当我计算 (get-setf-expansion ''())
时会发生什么 你明白为什么 (macroexpand-1 '(push 3 '())
的扩展变成了:
(let* ((tmp (cons 3 '())))
(funcall #'(setf quote) tmp '()))
还有你异常错误消息的来源。
宏 PUSH
的文档说:
push item place => new-place-value
地方是广义引用:变量、数组槽、结构槽、CLOS对象槽等等。它们记录在此处:CLHS 5.1 Generalized Reference
广义是什么意思?在 Common Lisp 中,places 是一个超越变量或槽等简单引用的概念。它甚至是用户可扩展的 -> 可以定义新类型的 places.
NIL 作为一个地方
NIL
(与 ()
相同)记录为 常量变量.
因此这样的事情失败了:
(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)
必须使用结果列表:将其存储在某处,将其传递给函数,...