Ros*_*oss 6 lisp function common-lisp parameter-passing pass-by-reference
我正在尝试将列表传递给Lisp中的函数,并在函数内更改该列表的内容,而不会影响原始列表.我已经读过Lisp是按值传递的,这是真的,但还有其他事情我不太明白.例如,此代码按预期工作:
(defun test ()
(setf original '(a b c))
(modify original)
(print original))
(defun modify (n)
(setf n '(x y z))
n)Run Code Online (Sandbox Code Playgroud)
如果你调用(测试),即使(修改)返回(xyz),它也会打印(abc).
但是,如果您尝试仅更改列表的一部分,则无法正常工作.我认为这与列表中的内容在内存中相同或者类似的内容有关?这是一个例子:
(defun test ()
(setf original '(a b c))
(modify original)
(print original))
(defun modify (n)
(setf (first n) 'x)
n)Run Code Online (Sandbox Code Playgroud)
然后(测试)打印(xbc).那么如何更改函数中list参数的某些元素,就好像该列表是该函数的本地列表一样?
Rai*_*wig 12
Lisp列表基于cons单元格.变量就像cons细胞(或其他Lisp对象)的指针.更改变量不会更改其他变量.在所有有缺陷细胞参考的地方都可以看到改变缺陷细胞.
一本好书是Touretzky,Common Lisp:一个温和的符号计算简介.
还有一些软件可以绘制列表和缺陷单元格.
如果将列表传递给这样的函数:
(modify (list 1 2 3))
Run Code Online (Sandbox Code Playgroud)
然后,您有三种不同的方式来使用该列表:
净细胞的破坏性修饰
(defun modify (list)
(setf (first list) 'foo)) ; This sets the CAR of the first cons cell to 'foo .
Run Code Online (Sandbox Code Playgroud)
结构共享
(defun modify (list)
(cons 'bar (rest list)))
Run Code Online (Sandbox Code Playgroud)
Above返回一个与传入列表共享结构的列表:其余元素在两个列表中都相同.
仿形
(defun modify (list)
(cons 'baz (copy-list (rest list))))
Run Code Online (Sandbox Code Playgroud)
上面的功能BAZ类似于BAR,但没有共享列表单元,因为列表被复制.
毋庸置疑,通常应该避免破坏性修改,除非有真正的理由去做(比如在值得的时候节省内存).
笔记:
永远不会破坏性地修改文字常量列表
不要做:(让((l'(abc)))(setf(first l)'bar))
原因:列表可能是写保护的,或者可能与其他列表共享(由编译器安排)等.
也:
介绍变量
像这样
(let ((original (list 'a 'b 'c)))
(setf (first original) 'bar))
Run Code Online (Sandbox Code Playgroud)
或者像这样
(defun foo (original-list)
(setf (first original-list) 'bar))
Run Code Online (Sandbox Code Playgroud)
永远不要SETF一个未定义的变量.
SETF修改一个地方. n可以是一个地方.列表的第一个元素n也指向一个地方.
在这两种情况下,持有的列表original都modify作为参数传递给它n.这意味着,original在功能上test和n在功能modify现在持有相同的列表,这意味着这两个original和n现在都指向它的第一个元素.
SETF n在第一种情况下修改后,它不再指向该列表而是指向新列表.指向的列表original不受影响.然后返回新列表modify,但由于此值未分配给任何内容,因此它将逐渐消失,并且很快将被垃圾回收.
在第二种情况下,SETF不会修改n,但列表的第一个元素n指向.这是同一个列表所original指向的,因此,之后,您也可以通过此变量查看修改后的列表.
要复制列表,请使用COPY-LIST.
它与C中的这个示例几乎相同:
void modify1(char *p) {
p = "hi";
}
void modify2(char *p) {
p[0] = 'h';
}
Run Code Online (Sandbox Code Playgroud)
在两种情况下都会传递一个指针,如果你改变了指针,你就会改变指针值的参数副本(它在堆栈上),如果改变了内容,你就会改变指向的任何对象的值. .