Skip to content

LISP - 合并序列

Common Lisp 提供了一套丰富的函数,用于操作序列(sequences)。序列是元素的有序集合,最常见的两种类型是列表(lists)和向量(vectors)。本章探讨了合并和组合这些序列的现代和惯用方法。

连接序列最强大和灵活的工具是 concatenate 函数。它可以将多个不同类型的序列合并为一个指定类型的新序列。这避免了手动、易出错的函数。

示例:合并列表、向量和字符串

Section titled “示例:合并列表、向量和字符串”
(let ((list1 '(1 2 3))
(vector1 #(4 5 6))
(list2 '(7 8)))
;; 将两个列表连接成一个新列表
(print (concatenate 'list list1 list2))
;; -> (1 2 3 7 8)
;; 将两个向量连接成一个新向量
(print (concatenate 'vector vector1 #(7 8 9)))
;; -> #(4 5 6 7 8 9)
;; 将一个列表和一个向量连接成一个新向量
(print (concatenate 'vector list1 vector1))
;; -> #(1 2 3 4 5 6)
;; 连接字符串(字符串是字符向量)
(print (concatenate 'string "Hello, " "World!"))
;; -> "Hello, World!"
)

最佳实践:当您需要创建新序列时,优先使用 concatenate 而不是 append。append 专门用于列表,并且效率可能较低,因为它必须复制除最后一个列表参数之外的所有列表。

交错(或称为“压缩”,zipping)是指通过以交替顺序从两个或更多源序列中取出元素来创建新序列。虽然没有一个单一的标准函数可以完成此操作,但可以使用递归或迭代优雅地实现它。

递归方法简洁明了,并展示了核心列表处理原理。

(defun interleave (list1 list2)
"创建一个新列表,交替取自 list1 和 list2 的元素。"
(cond ((null list1) list2)
((null list2) list1)
(t (list* (first list1) (first list2)
(interleave (rest list1) (rest list2))))))
(print (interleave '(a c e) '(b d f g h)))
;; -> (A B C D E F G H)

对于非常长的列表,使用强大的 loop 宏的迭代方法可能更高效,并避免潜在的栈溢出问题。

(defun interleave-iterative (list1 list2)
(loop for i in list1
for j in list2
collect i
collect j))
(print (interleave-iterative '(a c e) '(b d f)))
;; -> (A B C D E F)
;; 注意:基于 loop 的版本在较短列表耗尽时停止。

一个常见任务是将两个已排序的序列合并为一个新的、单一的排序序列。执行此操作的惯用且最有效的方法是使用内置的 merge 函数。

(let ((sorted-list1 '(1 5 9))
(sorted-list2 '(2 3 10 12)))
(let ((merged-result (merge 'list sorted-list1 sorted-list2 #'<)))
(print merged-result))
)
;; -> (1 2 3 5 9 10 12)

merge 函数非常灵活:

  • 结果类型:第一个参数指定结果序列的类型(例如,'list 或 'vector)。
  • 谓词:第四个参数是一个谓词函数(如 #<),用于确定排序顺序。
  • 键:它还接受一个 :key 参数,用于合并复杂对象的序列,就像 sort 函数一样。

在实际项目中,您将使用以下工具管理代码:

  • ASDF (Another System Definition Facility):用于定义您的项目、其文件及其依赖项。
  • Quicklisp:用于下载和管理第三方库。
  • Lisp 实现:例如 SBCL (Steel Bank Common Lisp) 以获得高性能,或 CCL (Clozure CL) 以获得快速编译时间。
  • 交互式环境:例如带 SLIME 的 Emacs 或带 Alive 的 VS Code,以获得强大的 REPL 驱动开发体验。