更改列表的副本而不更改Lisp中的原始副本

时间:2014-10-09 13:43:33

标签: list lisp

如何在不更改Common Lisp中原始列表元素的情况下更改列表副本的元素?

3 个答案:

答案 0 :(得分:3)

copy-list复制其参数列表的顶级结构。如果您打算通过手术修改内部值,您还需要复制它们。

[3]> (defvar a (list (list 1 2) (list 3 4) 5 (list 6)))
((1 2) (3 4) 5 (6))
[4]> (defvar b (copy-list a))
B
[5]> b
((1 2) (3 4) 5 (6))
[6]> (setf (third b) 55)
55
[7]> b
((1 2) (3 4) 55 (6))
[8]> a
((1 2) (3 4) 5 (6))     ;; top level value changed independently 
[9]> (setf (second (second b)) 44)
44
[10]> b
((1 2) (3 44) 55 (6))
[11]> a
((1 2) (3 44) 5 (6))    ;; deeper change reflected in the original

因此,在进行更深层次的更改之前,请先制作更深层次的副本,(setf (second b) (copy-list (second a))),首先:

[12]> (setf (second b) (copy-list (second a)))
(3 44)
[13]> (setf (second (second b)) 444)
444
[14]> b
((1 2) (3 444) 55 (6))
[15]> a
((1 2) (3 44) 5 (6))

答案 1 :(得分:1)

使用COPY-LIST复制列表。您可以删除或添加新列表的元素,旧列表不会更改。

答案 2 :(得分:1)

您需要在要替换的元素之前创建新的cons。例如

; example
(defparameter *test* '(1 2 (3 4 5 6) 7 8))

;; change 4 in the structure in *test* to 99
(list* (car *test*) 
       (cadr *test*)
       (list* (caaddr *test*)
              99               ; the actual change
              (cdaddr *test*)) ; shared sublist tail
       (cdddr *test*))         ; shared tail
; ==> (1 2 (3 99 4 5 6) 7 8)

这里子列表的结尾和主列表的结尾共享结构,因为它不需要更改。

如何搜索树并将一个子树替换为另一个子树:

;; replace all occurences of target in source with replacement
(defun find-replace (source target replacement) 
  (cond ((equal source target) replacement)                ;; equal, return replacement
        ((not (consp source)) source)                      ;; not equal && not pair, return source
        (t (cons (find-replace (car source) target replacement) ;; recurse
                 (find-replace (cdr source) target replacement)))))

(find-replace *test* 4 99)               ; ==> (1 2 (3 99 4 5 6) 7 8)
(find-replace *test* '(3 4 5 6) "banan") ; ==> (1 2 "banan" 7 8)