Skip to content

LISP - Arrays

While Lisp is famous for its lists, arrays are a fundamental and high-performance data structure for storing collections of elements. An array stores its elements in a contiguous block of memory, allowing for very fast, constant-time access to any element by its index. Common Lisp supports single and multi-dimensional arrays.

Unlike lists, where accessing the Nth element requires traversing N-1 elements, accessing any element in an array takes the same amount of time. This makes them ideal for scenarios requiring random access.

The primary function for creating arrays is make-array. To access elements, we use the aref function (short for array reference).

A one-dimensional array is often called a vector. Let’s create a vector to store a list of scores.

;; Define a parameter to hold a vector of size 5.
;; We can initialize it directly with :initial-contents.
(defparameter *scores* (make-array 5 :initial-contents '(98 85 77 92 100)))
;; Access the element at index 2 (the third element).
;; Lisp arrays are 0-indexed.
(format t "The third score is: ~a~%" (aref *scores* 2))
;; Modify the element at index 2.
(setf (aref *scores* 2) 80)
;; Print the entire array.
(format t "Updated scores: ~a~%" *scores*)
The third score is: 77
Updated scores: #(98 85 80 92 100)

Multi-dimensional arrays are specified with a list of dimensions.

;; Create a 2x3 matrix (2 rows, 3 columns)
(defparameter *matrix*
(make-array '(2 3) :initial-contents '((1 2 3)
(4 5 6))))
;; Access the element in the first row (index 0) and third column (index 2)
(format t "Element at (0, 2): ~a~%" (aref *matrix* 0 2))
;; Print the matrix representation
(format t "The matrix:~%~a~%" *matrix*)
Element at (0, 2): 3
The matrix:
#2A((1 2 3) (4 5 6))

The make-array function is highly versatile. Its full syntax includes several keyword arguments that control the array’s properties:

(make-array dimensions &key :element-type :initial-element
:initial-contents :adjustable
:fill-pointer :displaced-to
:displaced-index-offset)

Here are the most important keyword arguments:

ArgumentDescription
dimensionsA number for a vector, or a list of numbers for a multi-dimensional array. E.g., 5 or '(2 3).
:element-typeSpecifies the type of elements the array can hold. Defaults to T (any type). Using a more specific type like fixnum or single-float can significantly improve performance.
:initial-elementA value used to fill every cell of the new array. Cannot be used with :initial-contents.
:initial-contentsA nested sequence of elements used to populate the array. Its structure must match the dimensions.
:adjustableA boolean (t or nil). If t, the array’s dimensions can be changed later with adjust-array.
:fill-pointerIf set to t, creates a vector with a fill-pointer, making it easy to add or remove elements. Useful for dynamic collections. (See the Adjustable Arrays chapter).
:displaced-toCreates a new array that shares its content with another array. This allows for zero-copy ‘views’ into data.
;; A specialized array for performance. This array can only hold integers.
(defparameter *int-vector* (make-array 5 :element-type 'fixnum :initial-element 0))
(format t "Integer vector: ~a~%" *int-vector*)
;; A string is just a vector of characters.
(defparameter *char-vector* (make-array 5 :element-type 'character :initial-contents '(#\h #\e #\l #\l #\o)))
(format t "Character vector is a string: ~a~%" *char-vector*)
(terpri)
;; --- Displaced Array Example ---
(defparameter *source-data* (make-array 10 :initial-contents '(0 1 2 3 4 5 6 7 8 9)))
;; Create a "view" of the middle 4 elements of *source-data*
;; This view starts at index 3 of the source.
(defparameter *view* (make-array 4 :displaced-to *source-data* :displaced-index-offset 3))
(format t "Source data: ~a~%" *source-data*)
(format t "Displaced view: ~a~%" *view*)
;; Modifying the view also modifies the source array!
(setf (aref *view* 0) 99)
(format t "Source data after modification: ~a~%" *source-data*)
Integer vector: #(0 0 0 0 0)
Character vector is a string: "hello"
Source data: #(0 1 2 3 4 5 6 7 8 9)
Displaced view: #(3 4 5 6)
Source data after modification: #(0 1 2 99 4 5 6 7 8 9)
  • Arrays vs. Lists: Use arrays for fixed-size collections or when you need fast, random access. Use lists for collections that frequently change size at the beginning, or when sequential processing is the primary use case.
  • Specialize for Performance: If all elements in an array are of the same specific type (e.g., integer, single-float), declare it with :element-type. The Lisp compiler can generate much more efficient machine code for these specialized arrays.
  • Use vector for Simplicity: If you just need a simple 1D array (vector), you can use the (vector ...) shorthand: (defparameter *my-vec* (vector 1 2 3)) is equivalent to (make-array 3 :initial-contents '(1 2 3)).