Skip to content

LISP - Creating Vectors

A vector is a one-dimensional array in Common Lisp. Unlike lists, which are optimized for adding/removing elements at the head, vectors provide fast, constant-time (O(1)) access to any element by its index. This makes them ideal for random-access data storage.

Common Lisp offers three primary ways to create vectors, each suited for different situations.

Use the vector function when you know the elements of the vector at the time of creation.

;; Create a vector of numbers
(vector 10 20 30) ; => #(10 20 30)
;; Create a vector of mixed types
(vector "apple" 3.14 t) ; => #("apple" 3.14 T)

The #(...) syntax is a concise, literal way to define a vector. It’s evaluated by the Lisp reader before the code is even compiled, making it very efficient for constant data.

;; The reader macro is often preferred for constant vectors
(defparameter *my-constants* #(1 2 3))
(defparameter *fruit* #("apple" "banana" "orange"))

3. Using make-array for Maximum Flexibility

Section titled “3. Using make-array for Maximum Flexibility”

make-array is the most powerful tool for creating vectors (and multi-dimensional arrays). It allows you to specify the size, an initial value for all elements, the element type, and whether the vector should be adjustable.

;; Create a vector of size 5, with each element initialized to 0
(make-array 5 :initial-element 0) ; => #(0 0 0 0 0)
;; Create a vector from a list of initial contents
(make-array 3 :initial-contents '(1 2 3)) ; => #(1 2 3)
;; Create a specialized vector that can only hold strings
(make-array 4 :element-type 'string :initial-element "empty")
; => #("empty" "empty" "empty" "empty")

For situations where you need a dynamic, growable array (like std::vector in C++ or ArrayList in Java), you can create an adjustable vector with a fill pointer.

The :fill-pointer keeps track of the number of active elements, and :adjustable t allows the vector to be resized if it runs out of space. You add elements using vector-push-extend.

;; Create a dynamic vector with an initial capacity of 5
(defparameter *dynamic-vector*
(make-array 5 :adjustable t :fill-pointer 0))
(print *dynamic-vector*)
(format t "Initial length (fill-pointer): ~a~%" (fill-pointer *dynamic-vector*))
;; Add elements to the vector
(vector-push-extend 'a *dynamic-vector*)
(vector-push-extend 'b *dynamic-vector*)
(print *dynamic-vector*)
(format t "New length (fill-pointer): ~a~%" (fill-pointer *dynamic-vector*))
;; Access an element using AREF
(format t "First element: ~a~%" (aref *dynamic-vector* 0))

Executing this code demonstrates the process:

#()
Initial length (fill-pointer): 0
#(A B)
New length (fill-pointer): 2
First element: A
  • Use Lists for: Stacks, sequential processing, and when you frequently add or remove elements from the beginning of the collection. Their strength is cons, first, and rest operations.
  • Use Vectors for: Random access by index, fixed-size collections, or dynamic arrays where you typically add elements to the end. Their strength is the aref operation.