Skip to content

LISP - 序列连接

Common Lisp 提供了强大的通用函数来操作序列(sequence)。序列可以是列表(list)、向量(vector)或字符串(string)。concatenate 函数是一个多功能的工具,用于将多个序列组合成一个新序列。

(concatenate result-type &rest sequences)
  • result-type:一个类型说明符符号,例如 'list、'vector、'string,指示要创建的新序列的类型。
  • sequences:一个或多个要组合的序列。它们无需是相同类型。

一个类型为 result-type 的新序列,其中包含输入 sequences 中所有元素按给定顺序排列的内容。

最常见的用例是组合两个或多个列表。

(let ((list1 '(1 2 3))
(list2 '(4 5 6)))
(concatenate 'list list1 list2))
(1 2 3 4 5 6)

您可以灵活地混合列表、向量和字符串,并指定所需的输出类型。

;; Combine a list and a vector into a new vector
(concatenate 'vector '(a b c) #(d e f))
;; Combine a list and a vector into a new list
(concatenate 'list '(a b c) #(d e f))
#(A B C D E F)
(A B C D E F)

虽然 concatenate 对于字符串来说完美适用,但 format 函数通常被认为更符合习惯用法且更具可读性。

;; Using concatenate
(concatenate 'string "Hello, " "Lisp " "World!")
;; A more idiomatic way using format
(format nil "Hello, ~a World!" "Lisp")
"Hello, Lisp World!"

concatenate 是一个很好的通用工具,但了解其性能特征很重要。它总是分配一个新的序列。如果您通过在循环中重复添加元素来构建大型序列,这可能会效率低下。

构建列表的一种常见且高效的模式是,将元素 push 到一个临时列表,并在最后 nreverse 该列表。push 非常快,而 nreverse 是一种快速的原地反转操作。

(let ((result '()))
(dolist (i '(1 2 3 4 5))
(push (* i i) result)) ; Pushes to the front
(nreverse result)) ; Reverses the list to the correct order
(1 4 9 16 25)

对于构建向量,可以创建一个带填充指针(fill pointer)的可调整向量(adjustable vector),并使用 vector-push-extend。

(let ((vec (make-array 0 :adjustable t :fill-pointer 0)))
(dotimes (i 5)
(vector-push-extend (* i i) vec))
vec)
#(0 1 4 9 16)