Skip to content

LISP - Sequence Concatenation

Common Lisp provides powerful, generic functions for operating on sequences. A sequence can be a list, a vector, or a string. The concatenate function is a versatile tool for combining multiple sequences into a new one.

(concatenate result-type &rest sequences)
  • result-type: A type specifier symbol, such as 'list, 'vector, 'string, indicating the type of the new sequence to be created.
  • sequences: One or more sequences to be combined. They do not need to be of the same type.

A new sequence of type result-type containing all elements from the input sequences in the given order.

The most common use case is combining two or more lists.

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

You can flexibly mix lists, vectors, and strings, and specify the desired output type.

;; 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)

While concatenate works perfectly for strings, the format function is often considered more idiomatic and readable for this purpose.

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

Performance Considerations and Alternatives

Section titled “Performance Considerations and Alternatives”

concatenate is a great general-purpose tool, but it’s important to understand its performance characteristics. It always allocates a new sequence. If you are building up a large sequence by repeatedly adding elements in a loop, this can be inefficient.

A common and highly efficient pattern for building a list is to push elements onto a temporary list and nreverse it at the end. push is very fast, and nreverse is a fast, in-place reversal.

(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)

For building vectors, create an adjustable vector with a fill pointer and use 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)