如何在不修改原始列表内容的情况下修改列表的内容

时间:2016-03-18 00:46:21

标签: lisp

尝试创建列表的副本。我使用了copy-list但是这会修改原始列表,因此我无法使用copy-list和copy-tree无法正常工作。任何建议将不胜感激

(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 

3 个答案:

答案 0 :(得分:0)

您的示例代码存在多个问题。

评论中已经指出了第一个; copy-list复制给定的列表,但不复制该列表的元素。这一事实意味着您应该使用copy-tree代替。

第二个是setf没有修改你认为你正在修改的地方。

使用您的示例代码,当我第一次评估(switch-variable ’b *list_a*)时,结果是列表((A NIL) (B) (C T) (D NIL)),我们可以注意到它与列表((A NIL) (B NIL) (C T) (D NIL))不同

当我第二次评估(switch-variable ’b *list_a*)时,结果是列表((A NIL) (B . T) (C T) (D NIL)),我们可以注意到它与列表((A NIL) (B T) (C T) (D NIL))不同

根据(B T),考虑列表(B . T)cons对之间的差异。第一个可以通过评估(cons 'b (cons t nil))来构建。第二个可以通过评估(cons 'b t)来构建。这应该会引导您直接解决问题,并解决问题。

在您的评论中提到您使用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

当我遇到的问题开始看起来可能是在语言提供的函数/表单中工作不太正确时,做上述小型简单测试以确认或纠正我对这些的理解非常有用函数/表单,因为它们通常会导致对函数/表单的功能有正确的理解,或者导致我在代码中出错。您也可以自己发现这种情况。

答案 1 :(得分:0)

最好使用循环在一次传递中复制和修改列表。或者地图,但我更喜欢循环。

(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)。

答案 2 :(得分:0)

就配对而言,你所拥有的基本上是以下内容(我试图接近" cons盒"但是在相同的空间中,我已经完成了内部列表列表):

[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
  v            v            v            v
  (A NIL)      (B T)        (C T)        (D NIL)

运行copy-list后,您拥有以下内容(top是原始版,bottom是copy-list的返回值):

[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> 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

此时,您已修改了其中一个您尚未复制的内部列表。如果您使用copy-tree代替,则您会遇到以下情况(top是原始,bottom是copy-tree返回):

[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL
  v            v            v            v
  (A NIL)      (B T)        (C T)        (D NIL)

  (A NIL)      (B T)        (C T)        (D NIL)
  ^            ^            ^            ^
[ | . --]--> [ | . --]--> [ | . --]--> [ | . --]--> NIL

此时,内部列表也会被复制,您可以根据需要对其进行破坏性修改,而无需修改原始列表。