我试图将列表传递给 Lisp 中的函数,并在不影响原始列表的情况下在函数内更改该列表的内容。我读过 Lisp 是按值传递的,这是真的,但还有一些我不太明白的事情。例如,此代码按预期工作:
(defun test ()
(setf original '(a b c))
(modify original)
(print original))
(defun modify (n)
(setf n '(x y z))
n)
如果您调用 (test),它会打印 (a b c),即使 (modify) 返回 (x y z)。
但是,如果您尝试仅更改列表的一部分,它就不会那样工作。我认为这与具有相同内容的列表在任何地方的内存中都相同或类似的东西有关?这是一个例子:
(defun test ()
(setf original '(a b c))
(modify original)
(print original))
(defun modify (n)
(setf (first n) 'x)
n)
然后(测试)打印 (x b c)。那么,如何更改函数中列表参数的某些元素,就好像该列表是该函数的本地元素一样?
最佳答案
Lisp 列表基于 cons 单元格。变量就像指向 cons 单元(或其他 Lisp 对象)的指针。改变一个变量不会改变其他变量。更改 cons 单元格将在所有引用这些 cons 单元格的地方可见。
Touretzky 是一本好书,Common Lisp: A Gentle Introduction to Symbolic Computation .
还有绘制列表树和 cons 单元格的软件。
如果将列表传递给这样的函数:
(modify (list 1 2 3))
那么你可以通过三种不同的方式使用列表:
cons 细胞的破坏性修饰
(defun modify (list)
(setf (first list) 'foo)) ; This sets the CAR of the first cons cell to 'foo .
结构分享
(defun modify (list)
(cons 'bar (rest list)))
上面返回的列表与传入的列表共享结构:两个列表中的其余元素相同。
复制
(defun modify (list)
(cons 'baz (copy-list (rest list))))
上述函数 BAZ 与 BAR 类似,但没有共享列表单元格,因为列表是复制的。
不用说,通常应该避免破坏性修改,除非有真正的理由这样做(比如在值得时节省内存)。
注意事项:
永远不要破坏性地修改文字常量列表
不要做:(let ((l '(a b c))) (setf (first l) 'bar))
原因:列表可能被写保护,也可能与其他列表共享(由编译器安排)等
还有:
引入变量
像这样
(let ((original (list 'a 'b 'c)))
(setf (first original) 'bar))
或者像这样
(defun foo (original-list)
(setf (first original-list) 'bar))
永远不要 SETF 一个 undefined variable 。
关于function - 在 common-lisp 中,如何在不更改原始列表的情况下从函数中修改部分列表参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1484335/