如何在不更改Common Lisp中原始列表元素的情况下更改列表副本的元素?
答案 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)