LISP - Arrays
Lisp - Working with Arrays
Section titled “Lisp - Working with 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.
Creating and Accessing Arrays
Section titled “Creating and Accessing Arrays”The primary function for creating arrays is make-array. To access elements, we use the aref function (short for array reference).
Example: A Simple 1D Array (Vector)
Section titled “Example: A Simple 1D Array (Vector)”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*)Output
Section titled “Output”The third score is: 77Updated scores: #(98 85 80 92 100)Example: A 2D Array (Matrix)
Section titled “Example: A 2D Array (Matrix)”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*)Output
Section titled “Output”Element at (0, 2): 3The matrix:#2A((1 2 3) (4 5 6))The make-array Function In-Depth
Section titled “The make-array Function In-Depth”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:
| Argument | Description |
|---|---|
dimensions | A number for a vector, or a list of numbers for a multi-dimensional array. E.g., 5 or '(2 3). |
:element-type | Specifies 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-element | A value used to fill every cell of the new array. Cannot be used with :initial-contents. |
:initial-contents | A nested sequence of elements used to populate the array. Its structure must match the dimensions. |
:adjustable | A boolean (t or nil). If t, the array’s dimensions can be changed later with adjust-array. |
:fill-pointer | If 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-to | Creates a new array that shares its content with another array. This allows for zero-copy ‘views’ into data. |
Example: Specialized and Displaced Arrays
Section titled “Example: Specialized and Displaced Arrays”;; 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*)Output
Section titled “Output”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)Best Practices
Section titled “Best Practices”- 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
vectorfor 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)).