推送不会将列表修改为函数参数

Push doesn't modify the list being a function argument

我是普通 lisp 的新手,所以希望有人能给我澄清一下:

假设我们有一个列表,想要添加一个带有 push 的项目来修改它:

CL-USER> (defparameter xx '(1 2 3))
XX
CL-USER> xx
(1 2 3)
CL-USER> (push 100 xx)
(100 1 2 3)
CL-USER> xx
(100 1 2 3)

符合预期。但是当我尝试对函数做同样的事情时,它不会修改列表:

CL-USER> (defun push-200 (my-list)
           (push 200 my-list))
PUSH-200
CL-USER> (push-200 xx)
(200 100 1 2 3)
CL-USER> xx
(100 1 2 3)

所以我试着像这样比较参数和我的列表:

CL-USER> (defun push-200 (my-list)
           (format t "~a" (eq my-list xx))
           (push 200 my-list))

WARNING: redefining COMMON-LISP-USER::PUSH-200 in DEFUN
PUSH-200
CL-USER> (push-200 xx)
T
(200 100 1 2 3)
CL-USER> xx
(100 1 2 3)

它说对象是相同的。 所以问题是:我在这里忽略了什么?

(defun push-200 (my-list)
  (push 200 my-list))

这会修改变量 my-list。该变量现在指向一个新的 cons.

基本上是:(setq my-list (cons 200 my-list).

(push-200 xx)

Common Lisp 将 xx 计算为一个值并将列表传递给 push-200xx不是传递的,而是它的值。因此,Lisp 系统无法修改 xx 以指向不同的缺点,因为它未通过。

这(本质上)与以下突出显示的问题相同:

(let ((a (list 2 3 4 5)))
  (let ((b a))
    (push 1 a)
    (list (eql a b)
          (eql (cdr a) b))))

当运行.

时,此表格应return(NIL T)

Rainer Joswig 撰写的内容(以及 Vatine 演示的内容)。只需将 (push <A> <B>) 替换为 (setq <B> (cons <A> <B>)),因为 push is a macro

"push item place => new-place-value"

并且,

"new-place-value [is] a list (the new value of place)"

注意 CLHS 没有说 "(更新的结构引用的地方)".

所以,地方的值改变了。在你的例子中,那个地方是局部变量 my-list。在 (setq ...) 之前是 eqxx,但在它之后显然是 而不是 。要实际更改列表结构,您可以使用 rplaca and ⁄ or rplacd

我们可以考虑在 Common Lisp 中传递参数,就像传递的是一个指向值的指针一样;指针本身通过 传递,即复制。所以 xx "points" 在某个列表中;并且最初 my-list "points" 在相同的内存位置。这就是初始 eq 测试成功的原因。