Lisp 如何在不修改原始列表内容的情况下修改列表内容
正在尝试创建列表的副本。我使用了复制列表,但这会修改原始列表,因此我无法使用复制列表,并且复制树不工作。如有任何建议,将不胜感激Lisp 如何在不修改原始列表内容的情况下修改列表内容,lisp,Lisp,正在尝试创建列表的副本。我使用了复制列表,但这会修改原始列表,因此我无法使用复制列表,并且复制树不工作。如有任何建议,将不胜感激 (defun switch-var (var list_a) (let ((temp (copy-list list_a))) (setf (cdr (assoc var temp)) (not (cdr (assoc var temp))))temp)) 例如,在lisp中,我将创建一个列表,然后调用switch var (setf *list_a* '((A
(defun switch-var (var list_a)
(let ((temp (copy-list list_a)))
(setf (cdr (assoc var temp)) (not (cdr (assoc var temp))))temp))
例如,在lisp中,我将创建一个列表,然后调用switch var
(setf *list_a* '((A NIL) (B T) (C T) (D NIL)))
* (switch-var ’b *list_a*)
;I will get this which is ok
((A NIL) (B NIL) (C T) (D NIL))
;but if i call it again
* (switch-var ’b *list_a*)
;I will get this which is not ok
((A NIL) (B T) (C T) (D NIL))
;so techincally I do not want to modify the original list_a
;in the function I just want to modify the temp
您的示例代码存在不止一个问题 第一个已经在评论中指出<代码>复制列表复制给定的列表,但不复制该列表的元素。这意味着您应该使用
copytree
第二个原因是setf并没有修改您似乎认为正在修改的位置
使用您的示例代码,当我第一次计算(开关变量'b*list_a*)
时,结果是list((a NIL)(b)(C T)(D NIL))
,我们可以注意到它不同于list((a NIL)(b NIL)(C T)(D NIL))
当我第二次计算(开关变量'b*list_a*)
时,结果是list((a NIL)(b.T)(C T)(D NIL))
,我们可以注意到它不同于list((a NIL)(b T)(C T)(D NIL))
考虑一下列表(bt)
与配对(bt)
在缺点方面的区别。第一个可以通过计算(cons'b(cons t nil))
来构建。第二个可以通过计算(cons'bt)
来构建。这将直接引导您解决问题,并解决问题
关于您在使用copy tree
时仍然存在问题的评论,您可以通过执行以下操作来确认copy tree
是否按预期工作:
CL-USER> (defvar *test-alist* '((a nil) (b t) (c t) (d nil)))
*TEST-ALIST*
CL-USER> (defun lists-share-p (list1 list2)
(intersection list1 list2 :test #'eq))
LISTS-SHARE-P
CL-USER> (lists-share-p *test-alist* (copy-list *test-alist*))
((D NIL) (C T) (B T) (A NIL))
CL-USER> (lists-share-p *test-alist* (copy-tree *test-alist*))
NIL
当我遇到一些问题,这些问题看起来可能在语言提供的函数/表单中工作不太正常时,执行类似于上述的小型简单测试来确认或纠正我对这些函数/表单的理解是非常有用的,因为它们通常会导致对函数/表单的正确理解,或者使我正确地理解代码中的错误。您自己可能也会发现这种情况。最好使用循环在一次过程中复制和修改列表。或者地图,但我更喜欢环
(defparameter *foo* '((a . nil)
(b . t)
(c . t)
(d . nil)))
(defun switch-variable (var list)
(loop
for (name . val) in list
collecting (cons name
(if (eql name var)
(not val)
val))))
(switch-variable 'b *foo*)
;=> ((A) (B) (C . T) (D))
(switch-variable 'b *foo*)
;=> ((A) (B) (C . T) (D))
对变量使用cons单元格而不是列表更有效。打印时,它不会显示NIL
,但这只是视觉效果(值仍然是NIL
)。就成对而言,基本上如下所示(我尝试近似“cons-box”,但在相同的空间中,我将内部列表与列表一样):
一旦运行了复制列表
,您就有了以下内容(顶部是原始的,底部是复制列表
的返回值):
然后浏览并修改复制的顶级列表:
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
v v v v
(A NIL) (B NIL) (C T) (D NIL)
^ ^ ^ ^
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
在这一点上,您已经修改了一个内部列表,您没有复制它。如果您使用了复制树
,则会出现以下情况(顶部是原始的,底部是从复制树
返回的):
此时,内部列表也会被复制,您可以随意对其进行破坏性修改,而无需修改原始列表。请修复代码格式,并解释复制列表
和复制树
的具体错误(包括完整错误消息&c)。您没有修改列表。您确实修改了列表的内容元素。好吧,但是如何在不影响原始内容的情况下修改列表的内容元素呢?我纠正了以前的错误,并进行了递归调用,现在我第二次调用cdr时,它不再是B。但是现在它是bt,但是使用树并不能修复不修改原始文件的问题。也许我不明白你的意思,但谢谢你的帮助。@BlackKnight我认为在这里递归并不重要,除非你想自己修改结果,而不是使用复制树。从复制树
返回的结果不应该与原始列表有任何共享结构,并且当我在给定代码中使用复制树
而不是复制列表
时,我看不到对原始列表的任何修改。我想可能有一个古老的定义,比如在你的问题中,你定义了开关变量
,然后继续调用开关变量
。
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
v v v v
(A NIL) (B T) (C T) (D NIL)
^ ^ ^ ^
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
v v v v
(A NIL) (B NIL) (C T) (D NIL)
^ ^ ^ ^
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
v v v v
(A NIL) (B T) (C T) (D NIL)
(A NIL) (B T) (C T) (D NIL)
^ ^ ^ ^
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL