首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在common-lisp中,如何在不更改原始列表的情况下从函数中修改列表参数的一部分?

在common-lisp中,如何在不更改原始列表的情况下从函数中修改列表参数的一部分?
EN

Stack Overflow用户
提问于 2009-09-27 20:01:33
回答 4查看 6.6K关注 0票数 7

我正在尝试将一个列表传递给Lisp中的一个函数,并在该函数中更改该列表的内容,而不会影响原始列表。我读到过Lisp是按值传递的,这是真的,但还有一些我不太理解的事情。例如,此代码按预期工作:

代码语言:javascript
复制
(defun test ()
    (setf original '(a b c))
    (modify original)
    (print original))
(defun modify (n)
    (setf n '(x y z))
    n)

如果调用(test),即使(modify)返回(x,y,z),它也会打印(a,b,c)。

但是,如果您只尝试更改列表的一部分,则不会以这种方式工作。我假设这与列表有相同的内容,在所有地方的内存中都是相同的,或者类似的东西有关。下面是一个示例:

代码语言:javascript
复制
(defun test ()
    (setf original '(a b c))
    (modify original)
    (print original))
(defun modify (n)
    (setf (first n) 'x)
    n)

然后(test)打印(x B c)。那么,如何更改函数中列表参数的某些元素,就好像该列表是该函数的本地列表一样?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-09-27 20:18:17

SETF修改一个位置。n可以是一个地方。n指向的列表的第一个元素也可以是一个位置。

在这两种情况下,original保存的列表都作为其参数n传递给modify。这意味着函数test中的original和函数modify中的n现在都拥有相同的列表,这意味着originaln现在都指向它的第一个元素。

在第一种情况下,SETF修改了n之后,它不再指向该列表,而是指向一个新列表。original指向的列表不受影响。然后,modify返回新的列表,但是由于这个值没有被赋值给任何东西,所以它逐渐消失,很快就会被垃圾回收。

在第二种情况下,SETF不修改n,而是修改n指向的列表的第一个元素。这与original指向的列表相同,因此,之后,您还可以通过此变量查看修改后的列表。

要复制列表,请使用COPY-LIST

票数 7
EN

Stack Overflow用户

发布于 2009-09-27 21:32:00

Lisp列表基于cons单元格。变量就像指向cons单元格(或其他Lisp对象)的指针。更改一个变量不会更改其他变量。更改cons单元格将在引用这些cons单元格的所有位置可见。

一本好书是Touretzky,Common Lisp: A Gentle Introduction to Symbolic Computation

还有一款软件可以绘制列表和cons单元格的树。

如果您将列表传递给如下所示的函数:

代码语言:javascript
复制
(modify (list 1 2 3))

那么你有三种不同的方式来使用这个列表:

cons细胞的破坏性修饰

代码语言:javascript
复制
(defun modify (list)
   (setf (first list) 'foo)) ; This sets the CAR of the first cons cell to 'foo .

structure共享

代码语言:javascript
复制
(defun modify (list)
   (cons 'bar (rest list)))

上面返回一个与传入的列表共享结构的列表:两个列表中的rest元素是相同的。

复制

代码语言:javascript
复制
(defun modify (list)
   (cons 'baz (copy-list (rest list))))

上面的函数BAZ类似于BAR,但没有共享列表单元格,因为列表是复制的。

不用说,通常应该避免破坏性修改,除非有真正的理由这样做(比如在值得的时候节省内存)。

备注:

从不破坏性地修改文字常量列表

不做:(let ((l '(a B C) (setf (first l) 'bar))

原因:该列表可能是写保护的,或者可能与其他列表共享(由编译器安排),等等。

另外:

引入变量

像这样

代码语言:javascript
复制
(let ((original (list 'a 'b 'c)))
   (setf (first original) 'bar))

或者像这样

代码语言:javascript
复制
(defun foo (original-list)
   (setf (first original-list) 'bar))

切勿设置未定义的变量。

票数 14
EN

Stack Overflow用户

发布于 2009-09-28 09:46:21

它与C语言中的这个示例几乎相同:

代码语言:javascript
复制
void modify1(char *p) {
    p = "hi";
}

void modify2(char *p) {
    p[0] = 'h';
}

在这两种情况下都会传递一个指针,如果你改变了指针,你就改变了指针值的参数副本(它在堆栈上),如果你改变了内容,你就改变了指向的任何对象的值。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1484335

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档