Common lisp (push x nil)VS(push x存储空列表的位置)

Common lisp (push x nil)VS(push x存储空列表的位置),common-lisp,Common Lisp,为什么不能直接将推送到'(1 2 3)或NIL之类的列表上 具体而言: 为什么可以这样做 > (let ((some-list nil)) (push 42 some-list)) (42) 但是不要做像这样的事情 (push 42 nil) 或 此实现背后的原因是什么?使用宏push第二个参数需要修改。以下是一些例子: 让我们创建两个变量: (defparameter *v* (list 2 4)) (defparameter *v-copy* *v*) 然后我们按0 (

为什么不能直接将
推送到
'(1 2 3)
NIL
之类的列表上

具体而言: 为什么可以这样做

> (let ((some-list nil))
    (push 42 some-list))

(42)
但是不要做像这样的事情

(push 42 nil)


此实现背后的原因是什么?

使用宏
push
第二个参数需要修改。以下是一些例子:

让我们创建两个变量:

(defparameter *v* (list 2 4))
(defparameter *v-copy* *v*)
然后我们按
0

(push 1 *v*) ; ==> (1 2 4)
*v-copy*     ; ==> (2 4) (unaltered)

; the reason is that the variable is changed, not its value
(macroexpand '(push 1 v))
; ==> (setq v (cons 1 v))
push
可以使用其他内容作为第二个参数。让我们尝试一个
cons

(push 3 (cdr *v-copy*))
*v-copy*  ; ==> (2 3 4)
; since the tail of *v* is the *v-copy* *v* is changed too
*v*       ; ==> (1 2 3 4)

(macroexpand-1 '(push 2 (cdr *v-copy*)))
; ==> (rplacd v (cons 2 (cdr *v-copy*)))
如果你的例子是有效的,它应该做什么呢?让我们先做nil:

(macroexpand '(push 42 nil))
; ==> (setq nil (cons 42 nil))
这就像对待任何其他变量一样对待
nil
,如果这起作用
nil
将不再是空列表。它应该是一个包含一个元素的列表,
42
,并且与
()
的值不同。通常,Lisp
nil
是一个常量,不能进行变异。我曾经创建过一个lisp,其中
nil
与其他变量一样是一个变量,并且重新定义了一个小的拼写错误
nil
,使得程序的行为异常,没有明显的原因

让我们试试你的文字引用列表

(macroexpand '(push 42 (quote (1 2 3))))
; ==> (let ((tmp (1 2 3)))
;      (funcall #'(setf quote) (cons 42 'tmp) tmp))

push
宏似乎没有区分特殊表单
quote
和那些设置了
setf
功能的类型。它不起作用,也没有意义。无论如何,如果将文本数据
'(1 2 3)
更改为
'(43 1 2 3)
时,您是否希望每次计算
(1 2 3)
时都能得到
(43 1 2 3)
?我想这将是改变常数的唯一真正效果。如果允许,则应允许您将
4
重新定义为
5
,以便对
4
(+2)
进行求值,显示结果
5

对于宏
push
第二个参数需要修改。以下是一些例子:

让我们创建两个变量:

(defparameter *v* (list 2 4))
(defparameter *v-copy* *v*)
然后我们按
0

(push 1 *v*) ; ==> (1 2 4)
*v-copy*     ; ==> (2 4) (unaltered)

; the reason is that the variable is changed, not its value
(macroexpand '(push 1 v))
; ==> (setq v (cons 1 v))
push
可以使用其他内容作为第二个参数。让我们尝试一个
cons

(push 3 (cdr *v-copy*))
*v-copy*  ; ==> (2 3 4)
; since the tail of *v* is the *v-copy* *v* is changed too
*v*       ; ==> (1 2 3 4)

(macroexpand-1 '(push 2 (cdr *v-copy*)))
; ==> (rplacd v (cons 2 (cdr *v-copy*)))
如果你的例子是有效的,它应该做什么呢?让我们先做nil:

(macroexpand '(push 42 nil))
; ==> (setq nil (cons 42 nil))
这就像对待任何其他变量一样对待
nil
,如果这起作用
nil
将不再是空列表。它应该是一个包含一个元素的列表,
42
,并且与
()
的值不同。通常,Lisp
nil
是一个常量,不能进行变异。我曾经创建过一个lisp,其中
nil
与其他变量一样是一个变量,并且重新定义了一个小的拼写错误
nil
,使得程序的行为异常,没有明显的原因

让我们试试你的文字引用列表

(macroexpand '(push 42 (quote (1 2 3))))
; ==> (let ((tmp (1 2 3)))
;      (funcall #'(setf quote) (cons 42 'tmp) tmp))

push
宏似乎没有区分特殊表单
quote
和那些设置了
setf
功能的类型。它不起作用,也没有意义。无论如何,如果将文本数据
'(1 2 3)
更改为
'(43 1 2 3)
时,您是否希望每次计算
(1 2 3)
时都能得到
(43 1 2 3)
?我想这将是改变常数的唯一真正效果。如果允许,则应允许您将
4
重新定义为
5
,以便对
4
(+2)
进行求值,显示结果
5

PUSH
是一个位置修改宏。它
CONS
将一个值赋给存储在该位置的任何对象(变量是位置的一种),并将结果存储在该位置。如果你不想修改一个地方,你可以直接调用
CONS
。你希望
(push 42'(1 2 3))
做什么?@Rainer:我希望
push
首先检查第二个参数是
nil
还是一个列表,如果是,则返回
(CONS x nil)
(CONS x'(1 2 3))
。但正如jkiiski所指出的,我完全忽略了显而易见的:
cons
odes,这很好。对。推应该有副作用。因为它应该与变量一起工作,所以它需要是一个宏来执行此操作。
PUSH
是一个修改宏的地方。它
CONS
将一个值赋给存储在该位置的任何对象(变量是位置的一种),并将结果存储在该位置。如果你不想修改一个地方,你可以直接调用
CONS
。你希望
(push 42'(1 2 3))
做什么?@Rainer:我希望
push
首先检查第二个参数是
nil
还是一个列表,如果是,则返回
(CONS x nil)
(CONS x'(1 2 3))
。但正如jkiiski所指出的,我完全忽略了显而易见的:
cons
odes,这很好。对。推应该有副作用。因为它应该和变量一起工作,所以它需要是一个宏来完成。谢谢你的详细回答。我知道nil在CL中是一个文本/常量。我希望
push
检查第二个arg是否为
nil
,如果是,则返回类似
(cons x nil)
@Frank
push
的值不会用于它的返回值,而是通过改变一个位置而产生的副作用。在
(let((v nil))(push 1 v))
事件中,使用
push
的原因不存在了,因为您不使用
v
,它超出了范围。您可能已经编写了
(cons1nil)
。感谢您的详细回答。我知道nil在CL中是一个文本/常量。我希望
push
检查第二个arg是否为
nil
,如果是,则返回类似
(cons x nil)
@Frank
push
的值不会用于它的返回值,而是通过改变一个位置而产生的副作用。在
(let((v nil))(push 1v))
事件中,使用
push
的原因已经不存在了