Data Structures

We hope to give here a clear reference of the common data
structures. To really learn the language, you should take the time to
read other resources. The following ones, which we relied upon,
have many more details:

push is equivalent to (setf place (cons item place )) except that
the subforms of place are evaluated only once, and item is evaluated
before place.

There is no built-in function to add to the end of a list. It is a
more costly operation (have to traverse the whole list). So if you
need to do this: either consider using another data structure, either
just reverse your list when needed.

Sequences

Many of the sequence functions take keyword arguments. All keyword
arguments are optional and, if specified, may appear in any order.

Pay attention to the :test argument. It defaults to eql (for
strings, use :equal).

The :key argument should be passed either nil, or a function of one
argument. This key function is used as a filter through which the
elements of the sequence are seen. For instance, this:

(find x y :key 'car)

is similar to (assoc* x y): It searches for an element of the list
whose car equals x, rather than for an element which equals x
itself. If :key is omitted or nil, the filter is effectively the
identity function.

some, notany(test, sequence): return either the value of the test, or nil.

mismatch(sequence-a, sequence-b): Return position in sequence-a where
sequence-a and sequence-b begin to mismatch. Return NIL if they match
entirely. Other parameters: :from-end bool, :start1, :start2 and
their :end[1,2].

sort, stable-sort (sequence, test [, key function])

find, position (foo, sequence)

search (sequence-a, sequence-b)

Search sequence-b for a subsequence matching sequence-a. Return
position in sequence-b, or NIL. Has the from-end, end1/2 and others
parameters.

substitute, nsubstitute[if,if-not]

sort, stable-sort, merge

replace (sequence-a, sequence-b)

Replace elements of sequence-a with elements of
sequence-b.

remove, delete (foo sequence)

Make a copy of sequence without elements matching foo. Has
:start/end, :key and :count parameters.

delete is the recycling version of remove.

(remove "foo" '("foo" "bar" "foo") :test 'equal)
;; => ("bar")

see also remove-if[-not] below.

mapping (map, mapcar, remove-if[-not],…)

If you’re used to map and filter in other languages, you probably want
mapcar. But it only works on lists, so to iterate on vectors (and
produce either a vector or a list, use (map 'list function vector).

mapcar also accepts multiple lists with &rest more-seqs. The
mapping stops as soon as the shortest sequence runs out.

Note: cl21’s map is a generic mapcar for lists and vectors.

map takes the output-type as first argument ('list, 'vector or
'string):

Fset - immutable data structure

Arrays and vectors

Arrays have constant-time access characteristics.

They can be fixed or adjustable. A simple array is neither displaced
(using :displaced-to, to point to another array) nor adjustable
(:adjust-array), nor does it have a fill pointer (fill-pointer,
that moves when we add or remove elements).

A vector is an array with rank 1 (of one dimension). It is also a
sequence (see above).

A simple vector is a simple array that is also not specialized (it
doesn’t use :element-type to set the types of the elements).

Create an array, one or many dimensions

make-array(sizes-list :adjustable bool)

adjust-array(array, sizes-list, :element-type, :initial-element)

Access: aref (array i [j …])

aref(array i j k …) or row-major-aref(array i) equivalent to
(aref i i i …).

Transforming a vector to a list.

If you’re mapping over it, see the map function whose first parameter
is the result type.

Or use (coerce vector 'list).

Hash Table

Hash Tables are a powerful data structure, associating keys with
values in a very efficient way. Hash Tables are often preferred over
association lists whenever performance is an issue, but they introduce
a little overhead that makes assoc lists better if there are only a
few key-value pairs to maintain.

Alists can be used sometimes differently though:

they can be ordered

we can push cons cells that have the same key, remove the one in
front and we have a stack

they have a human-readable printed representation

they can be easily (de)serialized

because of RASSOC, keys and values in alists are essentially
interchangeable; whereas in hash tables, keys and values play very
different roles (as usual, see CL Recipes for more).

Creating a Hash Table

Hash Tables are created using the function
make-hash-table. It
has no required argument. Its most used optional keyword argument is
:test, specifying the function used to test the equality of keys.

If we are using the cl21 extension library, we can
create a hash table and add elements in the same time with the new
#H reader syntax:

(defparameter *my-hash* #H(:name "Eitaro Fukamachi"))

then we access an element with

(getf *my-hash* :name)

Getting a value from a Hash Table

The function
gethash
takes two required arguments: a key and a hash table. It returns two
values: the value corresponding to the key in the hash table (or nil
if not found), and a boolean indicating whether the key was found in
the table. That second value is necessary since nil is a valid value
in a key-value pair, so getting nil as first value from gethash
does not necessarily mean that the key was not found in the table.

Getting a key that does not exist with a default value

gethash has an optional third argument:

(gethash 'bar *my-hash* "default-bar")
;; => "default-bar"
;; NIL

Getting all keys or all values of a hash table

The
Alexandria
library (in Quicklisp) has the functions hash-table-keys and
hash-table-values for that.

Testing for the Presence of a Key in a Hash Table

The first value returned by gethash is the object in the hash table
that’s associated with the key you provided as an argument to
gethash or nil if no value exists for this key. This value can act
as a
generalized boolean if you want to test for the presence of keys.

Traversing a Hash Table

If you want to perform an action on each entry (i.e., each key-value
pair) in a hash table, you have several options:

You can use
maphash
which iterates over all entries in the hash table. Its first argument
must be a function which accepts two arguments, the key and the
value of each entry. Note that due to the nature of hash tables you
can’t control the order in which the entries are provided by
maphash (or other traversing constructs). maphash always returns
nil.

You can also use
with-hash-table-iterator,
a macro which turns (via
macrolet)
its first argument into an iterator that on each invocation returns
three values per hash table entry - a generalized boolean that’s true
if an entry is returned, the key of the entry, and the value of the
entry. If there are no more entries, only one value is returned -
nil.

;;; same hash-table as above
CL-USER> (with-hash-table-iterator (my-iterator *my-hash*)
(loop
(multiple-value-bind (entry-p key value)
(my-iterator)
(if entry-p
(print-hash-entry key value)
(return)))))
The value associated with the key FIRST-KEY is ONE
The value associated with the key SECOND-KEY is TWO
The value associated with the key THIRD-KEY is NIL
The value associated with the key NIL is NIL-VALUE
NIL

Note the following caveat from the HyperSpec: “It is unspecified what
happens if any of the implicit interior state of an iteration is
returned outside the dynamic extent of the with-hash-table-iterator
form such as by returning some closure over the invocation form.”

;;; same hash-table as above
CL-USER> (loop for key being the hash-keys of *my-hash*
do (print key))
FIRST-KEY
SECOND-KEY
THIRD-KEY
NIL
NIL
CL-USER> (loop for key being the hash-keys of *my-hash*
using (hash-value value)
do (format t "The value associated with the key ~S is ~S~%" key value))
The value associated with the key FIRST-KEY is ONE
The value associated with the key SECOND-KEY is TWO
The value associated with the key THIRD-KEY is NIL
The value associated with the key NIL is NIL-VALUE
NIL
CL-USER> (loop for value being the hash-values of *my-hash*
do (print value))
ONE
TWO
NIL
NIL-VALUE
NIL
CL-USER> (loop for value being the hash-values of *my-hash*
using (hash-key key)
do (format t "~&~A -> ~A" key value))
FIRST-KEY -> ONE
SECOND-KEY -> TWO
THIRD-KEY -> NIL
NIL -> NIL-VALUE
NIL

Performance Issues: The Size of your Hash Table

The make-hash-table function has a couple of optional parameters
which control the initial size of your hash table and how it’ll grow
if it needs to grow. This can be an important performance issue if
you’re working with large hash tables. Here’s an (admittedly not very
scientific) example with CMUCL pre-18d on
Linux:

The values for
hash-table-size
and
hash-table-rehash-size
are implementation-dependent. In our case, CMUCL chooses and initial
size of 65, and it will increase the size of the hash by 50 percent
whenever it needs to grow. Let’s see how often we have to re-size the
hash until we reach the final size…

The hash has to be re-sized 19 times until it’s big enough to hold
100,000 entries. That explains why we saw a lot of consing and why it
took rather long to fill the hash table. It also explains why the
second run was much faster - the hash table already had the correct
size.

Here’s a faster way to do it:
If we know in advance how big our hash will be, we can start with the right size:

That’s obviously much faster. And there was no consing involved
because we didn’t have to re-size at all. If we don’t know the final
size in advance but can guess the growth behaviour of our hash table
we can also provide this value to make-hash-table. We can provide an
integer to specify absolute growth or a float to specify relative
growth.

Also rather fast (we only needed one re-size) but much more consing
because almost the whole hash table (minus 65 initial elements) had to
be built during the loop.

Note that you can also specify the rehash-threshold while creating a
new hash table. One final remark: Your implementation is allowed to
completely ignore the values provided for rehash-size and
rehash-threshold…

To get a key, we have assoc (use :test 'equal when your keys are
strings, as usual). It returns the whole cons cell, so you may want to
use cdr or second to get the value. There is assoc-if, and
rassoc to get a cons cell by its value.

In the
Alexandria
library, see some functions like remove-from-plist, alist-plist,…

Plist

A property list is simply a list that alternates a key, a value, and
so on, where its keys are symbols (we can not set its :test). More
precisely, it first has a cons cell whose car is the key, whose
cdr points to the following cons cell whose car is the
value.

For example this plist:

(defparameter my-plist (list 'foo "foo" 'bar "bar"))

looks like this:

[o|o]---[o|o]---[o|o]---[o|/]
| | | |
FOO "foo" BAR "bar"

We access an element with getf (list elt) (it returns the value)
(the list comes as first element),