LISP - 列表修改
Lisp - 列表修改
Section titled “Lisp - 列表修改”在 Common Lisp 中,列表是典型的(或核心的)数据结构。修改列表是一个常见的任务,Lisp 为此提供了两类不同的函数:返回新的、已修改列表的**非破坏性(non-destructive)函数,以及直接修改原始列表内存结构的破坏性(destructive)**函数。
理解这种区别是熟练 Lisp 编程最关键的方面之一,因为它直接影响程序的正确性和性能。
非破坏性操作 vs. 破坏性操作
Section titled “非破坏性操作 vs. 破坏性操作”| 操作类型 | 描述 | 关键函数 |
|---|---|---|
| 非破坏性(函数式风格) | 这些函数创建并返回一个包含所需更改的新列表,而不会触及原始列表。这更安全,也更容易理解,尤其是在复杂的程序中。 | append, remove |
| 破坏性(命令式风格) | 这些函数就地修改原始列表的 cons 单元。这可能更快,内存效率更高,因为它避免了创建新对象。按照惯例,它们的名称通常包含 ‘n’ 或 ‘r’。 | nconc, delete, rplaca, rplacd, push, pop |
让我们比较 append(非破坏性)和 nconc(破坏性)。
main.lisp
Section titled “main.lisp”(defparameter *list-a* '(a b c))(defparameter *list-b* '(d e f))
;; 1. 非破坏性 APPEND(let ((new-list (append *list-a* *list-b*))) (format t "Result of APPEND: ~a~%" new-list) (format t "Original *list-a* is unchanged: ~a~%~%" *list-a*))
;; 2. 破坏性 NCONC(let ((modified-list (nconc *list-a* *list-b*))) (format t "Result of NCONC: ~a~%" modified-list) (format t "Original *list-a* is NOW MODIFIED: ~a~%" *list-a*))Result of APPEND: (A B C D E F)Original *list-a* is unchanged: (A B C)
Result of NCONC: (A B C D E F)Original *list-a* is NOW MODIFIED: (A B C D E F)警告:使用 nconc 会修改 *list-a* 的最后一个 cons 单元,使其指向 *list-b*。这是一个永久性更改。
从列表中移除
Section titled “从列表中移除”类似地,remove(非破坏性)与 delete(破坏性)形成对比。
main.lisp
Section titled “main.lisp”;; 1. 非破坏性 REMOVE(let ((original '(a b c b d))) (let ((new-list (remove 'b original))) (format t "Result of REMOVE: ~a~%" new-list) (format t "Original is unchanged: ~a~%~%" original)))
;; 2. 破坏性 DELETE(let ((original (list 'a 'b 'c 'b 'd))) ;; 我们必须使用 (list ...) 来创建一个可以安全修改的新列表。 (let ((modified-list (delete 'b original))) (format t "Result of DELETE: ~a~%" modified-list) (format t "Original is NOW MODIFIED: ~a~%" original)))Result of REMOVE: (A C D)Original is unchanged: (A B C B D)
Result of DELETE: (A C D)Original is NOW MODIFIED: (A C D)最佳实践:只在你刚创建且确定未在程序其他地方共享的列表上使用 delete。
从头部添加和移除:push 和 pop
Section titled “从头部添加和移除:push 和 pop”push 和 pop 是高效的破坏性宏,用于将列表作为栈来处理。
main.lisp
Section titled “main.lisp”(defparameter *stack* '(a b c))(format t "Initial stack: ~a~%" *stack*)
;; 将一个元素 PUSH 到列表头部(push 'x *stack*)(format t "After PUSH 'x: ~a~%" *stack*)
;; 从列表头部 POP 一个元素(let ((removed-item (pop *stack*))) (format t "Item popped: ~a~%" removed-item) (format t "After POP: ~a~%" *stack*))Initial stack: (A B C)After PUSH 'x: (X A B C)Item popped: XAfter POP: (A B C)低级修改:rplaca 和 rplacd
Section titled “低级修改:rplaca 和 rplacd”rplaca(替换 car)和 rplacd(替换 cdr)是低级的破坏性运算符,它们直接改变 cons 单元的指针。使用它们时务必极其谨慎。
main.lisp
Section titled “main.lisp”(let ((my-list (list 'a 'b 'c))) (format t "Original: ~a~%" my-list)
;; 替换第一个元素(第一个 cons 单元的 CAR 部分) (rplaca my-list 'x) (format t "After (rplaca my-list 'x): ~a~%" my-list)
;; 替换列表的其余部分(第一个 cons 单元的 CDR 部分) (rplacd my-list '(y z)) (format t "After (rplacd my-list '(y z)): ~a~%" my-list))Original: (A B C)After (rplaca my-list 'x): (X B C)After (rplacd my-list '(y z)): (X Y Z)