emacs.d/clones/lisp/lispcookbook.github.io/cl-cookbook/data-structures.html

1955 lines
67 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Data structures</title>
<meta charset="utf-8">
<meta name="description" content="A collection of examples of using Common Lisp">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<link rel="icon" href=
"assets/cl-logo-blue.png"/>
<link rel="stylesheet" href=
"assets/style.css">
<script type="text/javascript" src=
"assets/highlight-lisp.js">
</script>
<script type="text/javascript" src=
"assets/jquery-3.2.1.min.js">
</script>
<script type="text/javascript" src=
"assets/jquery.toc/jquery.toc.min.js">
</script>
<script type="text/javascript" src=
"assets/toggle-toc.js">
</script>
<link rel="stylesheet" href=
"assets/github.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1 id="title-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Data structures</h1>
<div id="logo-container">
<a href="index.html">
<img id="logo" src="assets/cl-logo-blue.png"/>
</a>
<div id="searchform-container">
<form onsubmit="duckSearch()" action="javascript:void(0)">
<input id="searchField" type="text" value="" placeholder="Search...">
</form>
</div>
<div id="toc-container" class="toc-close">
<div id="toc-title">Table of Contents</div>
<ul id="toc" class="list-unstyled"></ul>
</div>
</div>
<div id="content-container">
<h1 id="title-non-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Data structures</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
<p class="announce">
📹 <a href="https://www.udemy.com/course/common-lisp-programming/?couponCode=6926D599AA-LISP4ALL">NEW! Learn Lisp in videos and support our contributors with this 40% discount.</a>
</p>
<p class="announce-neutral">
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
</p>
<div id="content"
<p>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 resources, which we relied upon,
also have many more details:</p>
<ul>
<li><a href="http://gigamonkeys.com/book/they-called-it-lisp-for-a-reason-list-processing.html">Practical CL</a>, by Peter Seibel</li>
<li><a href="http://weitz.de/cl-recipes/">CL Recipes</a>, by E. Weitz, full of explanations and tips,</li>
<li>the
<a href="http://cberr.us/tech_writings/notes/common_lisp_standard_draft.html">CL standard</a>
with a nice TOC, functions reference, extensive descriptions, more
examples and warnings (i.e: everything). <a href="https://gitlab.com/vancan1ty/clstandard_build/-/blob/master/cl-ansi-standard-draft-w-sidebar.pdf">PDF mirror</a></li>
<li>a <a href="http://clqr.boundp.org/">Common Lisp quick reference</a></li>
</ul>
<p>Dont miss the appendix and when you need more data structures, have a
look at the
<a href="https://github.com/CodyReichert/awesome-cl#data-structures">awesome-cl</a>
list and <a href="https://quickdocs.org/-/search?q=data%20structure">Quickdocs</a>.</p>
<h2 id="lists">Lists</h2>
<h3 id="building-lists-cons-cells-lists">Building lists. Cons cells, lists.</h3>
<p><em>A list is also a sequence, so we can use the functions shown below.</em></p>
<p>The list basic element is the cons cell. We build lists by assembling
cons cells.</p>
<pre><code class="language-lisp">(cons 1 2)
;; =&gt; (1 . 2) ;; representation with a point, a dotted pair.
</code></pre>
<p>It looks like this:</p>
<pre><code>[o|o]--- 2
|
1
</code></pre>
<p>If the <code>cdr</code> of the first cell is another cons cell, and if the <code>cdr</code> of
this last one is <code>nil</code>, we build a list:</p>
<pre><code class="language-lisp">(cons 1 (cons 2 nil))
;; =&gt; (1 2)
</code></pre>
<p>It looks like this:</p>
<pre><code>[o|o]---[o|/]
| |
1 2
</code></pre>
<p>(ascii art by <a href="https://github.com/cbaggers/draw-cons-tree">draw-cons-tree</a>).</p>
<p>See that the representation is not a dotted pair ? The Lisp printer
understands the convention.</p>
<p>Finally we can simply build a literal list with <code>list</code>:</p>
<pre><code class="language-lisp">(list 1 2)
;; =&gt; (1 2)
</code></pre>
<p>or by calling quote:</p>
<pre><code class="language-lisp">'(1 2)
;; =&gt; (1 2)
</code></pre>
<p>which is shorthand notation for the function call <code>(quote (1 2))</code>.</p>
<h3 id="circular-lists">Circular lists</h3>
<p>A cons cell car or cdr can refer to other objects, including itself or
other cells in the same list. They can therefore be used to define
self-referential structures such as circular lists.</p>
<p>Before working with circular lists, tell the printer to recognise them
and not try to print the whole list by setting
<a href="http://clhs.lisp.se/Body/v_pr_cir.htm">*print-circle*</a>
to <code>T</code>:</p>
<pre><code class="language-lisp">(setf *print-circle* t)
</code></pre>
<p>A function which modifies a list, so that the last <code>cdr</code> points to the
start of the list is:</p>
<pre><code class="language-lisp">(defun circular! (items)
"Modifies the last cdr of list ITEMS, returning a circular list"
(setf (cdr (last items)) items))
(circular! (list 1 2 3))
;; =&gt; #1=(1 2 3 . #1#)
(fifth (circular! (list 1 2 3)))
;; =&gt; 2
</code></pre>
<p>The <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_list_l.htm#list-length">list-length</a>
function recognises circular lists, returning <code>nil</code>.</p>
<p>The reader can also create circular lists, using
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/02_dho.htm">Sharpsign Equal-Sign</a>
notation. An object (like a list) can be prefixed with <code>#n=</code> where <code>n</code>
is an unsigned decimal integer (one or more digits). The
label <code>#n#</code> can be used to refer to the object later in the
expression:</p>
<pre><code class="language-lisp">'#42=(1 2 3 . #42#)
;; =&gt; #1=(1 2 3 . #1#)
</code></pre>
<p>Note that the label given to the reader (<code>n=42</code>) is discarded after
reading, and the printer defines a new label (<code>n=1</code>).</p>
<p>Further reading</p>
<ul>
<li><a href="https://letoverlambda.com/index.cl/guest/chap4.html#sec_5">Let over Lambda</a> section on cyclic expressions</li>
</ul>
<h3 id="carcdr-or-firstrest-and-second-to-tenth">car/cdr or first/rest (and second… to tenth)</h3>
<pre><code class="language-lisp">(car (cons 1 2)) ;; =&gt; 1
(cdr (cons 1 2)) ;; =&gt; 2
(first (cons 1 2)) ;; =&gt; 1
(first '(1 2 3)) ;; =&gt; 1
(rest '(1 2 3)) ;; =&gt; (2 3)
</code></pre>
<p>We can assign <em>any</em> new value with <code>setf</code>.</p>
<h3 id="last-butlast-nbutlast-optional-n">last, butlast, nbutlast (&amp;optional n)</h3>
<p>return the last cons cell in a list (or the nth last cons cells).</p>
<pre><code class="language-lisp">(last '(1 2 3))
;; =&gt; (3)
(car (last '(1 2 3)) ) ;; or (first (last …))
;; =&gt; 3
(butlast '(1 2 3))
;; =&gt; (1 2)
</code></pre>
<p>In <a href="https://common-lisp.net/project/alexandria/draft/alexandria.html#Conses">Alexandria</a>, <code>lastcar</code> is equivalent of <code>(first (last …))</code>:</p>
<pre><code class="language-lisp">(alexandria:lastcar '(1 2 3))
;; =&gt; 3
</code></pre>
<h3 id="reverse-nreverse">reverse, nreverse</h3>
<p><code>reverse</code> and <code>nreverse</code> return a new sequence.</p>
<p><code>nreverse</code> is destructive. The N stands for <strong>non-consing</strong>, meaning
it doesnt need to allocate any new cons cells. It <em>might</em> (but in
practice, does) reuse and modify the original sequence:</p>
<pre><code class="language-lisp">(defparameter mylist '(1 2 3))
;; =&gt; (1 2 3)
(reverse mylist)
;; =&gt; (3 2 1)
mylist
;; =&gt; (1 2 3)
(nreverse mylist)
;; =&gt; (3 2 1)
mylist
;; =&gt; (1) in SBCL but implementation dependent.
</code></pre>
<h3 id="append">append</h3>
<p><code>append</code> takes any number of list arguments and returns a new list
containing the elements of all its arguments:</p>
<pre><code class="language-lisp">(append (list 1 2) (list 3 4))
;; =&gt; (1 2 3 4)
</code></pre>
<p>The new list shares some cons cells with the <code>(3 4)</code>:</p>
<p>http://gigamonkeys.com/book/figures/after-append.png</p>
<p><code>nconc</code> is the recycling equivalent.</p>
<h3 id="push-item-place">push (item, place)</h3>
<p><code>push</code> prepends <em>item</em> to the list that is stored in <em>place</em>, stores
the resulting list in <em>place</em>, and returns the list.</p>
<pre><code class="language-lisp">(defparameter mylist '(1 2 3))
(push 0 mylist)
;; =&gt; (0 1 2 3)
</code></pre>
<pre><code class="language-lisp">(defparameter x (a (b c) d))
;; =&gt; (A (B C) D)
(push 5 (cadr x))
;; =&gt; (5 B C)
x
;; =&gt; (A (5 B C) D)
</code></pre>
<p><code>push</code> is equivalent to <code>(setf place (cons item place ))</code> except that
the subforms of <em>place</em> are evaluated only once, and <em>item</em> is evaluated
before <em>place</em>.</p>
<p>There is no built-in function to <strong>add to the end of a list</strong>. 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 <code>reverse</code> your list when needed.</p>
<h3 id="pop">pop</h3>
<p>a destructive operation.</p>
<h3 id="nthcdr-index-list">nthcdr (index, list)</h3>
<p>Use this if <code>first</code>, <code>second</code> and the rest up to <code>tenth</code> are not
enough.</p>
<h3 id="carcdr-and-composites-cadr-caadr---accessing-lists-inside-lists">car/cdr and composites (cadr, caadr…) - accessing lists inside lists</h3>
<p>They make sense when applied to lists containing other lists.</p>
<pre><code class="language-lisp">(caar (list 1 2 3)) ==&gt; error
(caar (list (list 1 2) 3)) ==&gt; 1
(cadr (list (list 1 2) (list 3 4))) ==&gt; (3 4)
(caadr (list (list 1 2) (list 3 4))) ==&gt; 3
</code></pre>
<h3 id="destructuring-bind-parameter-list">destructuring-bind (parameter*, list)</h3>
<p>It binds the parameter values to the list elements. We can destructure
trees, plists and even provide defaults.</p>
<p>Simple matching:</p>
<pre><code class="language-lisp">(destructuring-bind (x y z) (list 1 2 3)
(list :x x :y y :z z))
;; =&gt; (:X 1 :Y 2 :Z 3)
</code></pre>
<p>Matching inside sublists:</p>
<pre><code class="language-lisp">(destructuring-bind (x (y1 y2) z) (list 1 (list 2 20) 3)
(list :x x :y1 y1 :y2 y2 :z z))
;; =&gt; (:X 1 :Y1 2 :Y2 20 :Z 3)
</code></pre>
<p>The parameter list can use the usual <code>&amp;optional</code>, <code>&amp;rest</code> and <code>&amp;key</code>
parameters.</p>
<pre><code class="language-lisp">(destructuring-bind (x (y1 &amp;optional y2) z) (list 1 (list 2) 3)
(list :x x :y1 y1 :y2 y2 :z z))
;; =&gt; (:X 1 :Y1 2 :Y2 NIL :Z 3)
</code></pre>
<pre><code class="language-lisp">(destructuring-bind (&amp;key x y z) (list :z 1 :y 2 :x 3)
(list :x x :y y :z z))
;; =&gt; (:X 3 :Y 2 :Z 1)
</code></pre>
<p>The <code>&amp;whole</code> parameter is bound to the whole list. It must be the
first one and others can follow.</p>
<pre><code class="language-lisp">(destructuring-bind (&amp;whole whole-list &amp;key x y z) (list :z 1 :y 2 :x 3)
(list :x x :y y :z z :whole whole-list))
;; =&gt; (:X 3 :Y 2 :Z 1 :WHOLE-LIST (:Z 1 :Y 2 :X 3))
</code></pre>
<p>Destructuring a plist, giving defaults:</p>
<p>(example from Common Lisp Recipes, by E. Weitz, Apress, 2016)</p>
<pre><code class="language-lisp">(destructuring-bind (&amp;key a (b :not-found) c
&amp;allow-other-keys)
(:c 23 :d "D" :a #\A :foo :whatever)
(list a b c))
;; =&gt; (#\A :NOT-FOUND 23)
</code></pre>
<p>If this gives you the will to do pattern matching, see
<a href="pattern_matching.html">pattern matching</a>.</p>
<h3 id="predicates-null-listp">Predicates: null, listp</h3>
<p><code>null</code> is equivalent to <code>not</code>, but considered better style.</p>
<p><code>listp</code> tests whether an object is a cons cell or nil.</p>
<p>and sequences predicates.</p>
<h3 id="ldiff-tailp-list-make-list-fill-revappend-nreconc-consp-atom">ldiff, tailp, list*, make-list, fill, revappend, nreconc, consp, atom</h3>
<pre><code class="language-lisp">(make-list 3 :initial-element "ta")
;; =&gt; ("ta" "ta" "ta")
</code></pre>
<pre><code class="language-lisp">(make-list 3)
;; =&gt; (NIL NIL NIL)
(fill * "hello")
;; =&gt; ("hello" "hello" "hello")
</code></pre>
<h3 id="member-elt-list">member (elt, list)</h3>
<p>Returns the tail of <code>list</code> beginning with the first element satisfying <code>eql</code>ity.</p>
<p>Accepts <code>:test</code>, <code>:test-not</code>, <code>:key</code> (functions or symbols).</p>
<pre><code class="language-lisp">(member 2 '(1 2 3))
;; (2 3)
</code></pre>
<h3 id="replacing-objects-in-a-tree-subst-sublis">Replacing objects in a tree: subst, sublis</h3>
<p><a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_substc.htm">subst</a> and
<code>subst-if</code> search and replace occurences of an element
or subexpression in a tree (when it satisfies the optional <code>test</code>):</p>
<pre><code class="language-lisp">(subst 'one 1 '(1 2 3))
;; =&gt; (ONE 2 3)
(subst '(1 . one) '(1 . 1) '((1 . 1) (2 . 2) (3 . 3)) :test #'equal)
;; ((1 . ONE) (2 . 2) (3 . 3))
</code></pre>
<p><a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_sublis.htm">sublis</a>
allows to replace many objects at once. It substitutes the objects
given in <code>alist</code> and found in <code>tree</code> with their new values given in
the alist:</p>
<pre><code class="language-lisp">(sublis '((x . 10) (y . 20))
'(* x (+ x y) (* y y)))
;; (* 10 (+ 10 20) (* 20 20))
</code></pre>
<p><code>sublis</code> accepts the <code>:test</code> and <code>:key</code> arguments. <code>:test</code> is a
function that takes two arguments, the key and the subtree.</p>
<pre><code class="language-lisp">(sublis '((t . "foo"))
'("one" 2 ("three" (4 5)))
:key #'stringp)
;; ("foo" 2 ("foo" (4 5)))
</code></pre>
<h2 id="sequences">Sequences</h2>
<p><strong>lists</strong> and <strong>vectors</strong> (and thus <strong>strings</strong>) are sequences.</p>
<p><em>Note</em>: see also the <a href="strings.html">strings</a> page.</p>
<p>Many of the sequence functions take keyword arguments. All keyword
arguments are optional and, if specified, may appear in any order.</p>
<p>Pay attention to the <code>:test</code> argument. It defaults to <code>eql</code> (for
strings, use <code>:equal</code>).</p>
<p>The <code>:key</code> 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:</p>
<pre><code class="language-lisp">(find x y :key 'car)
</code></pre>
<p>is similar to <code>(assoc* x y)</code>: It searches for an element of the list
whose car equals x, rather than for an element which equals x
itself. If <code>:key</code> is omitted or nil, the filter is effectively the
identity function.</p>
<p>Example with an alist (see definition below):</p>
<pre><code class="language-lisp">(defparameter my-alist (list (cons 'foo "foo")
(cons 'bar "bar")))
;; =&gt; ((FOO . "foo") (BAR . "bar"))
(find 'bar my-alist)
;; =&gt; NIL
(find 'bar my-alist :key 'car)
;; =&gt; (BAR . "bar")
</code></pre>
<p>For more, use a <code>lambda</code> that takes one parameter.</p>
<pre><code class="language-lisp">(find 'bar my-alist :key (lambda (it) (car it)))
</code></pre>
<pre><code class="language-lisp">(find 'bar my-alist :key ^(car %))
(find 'bar my-alist :key (lm (it) (car it)))
</code></pre>
<h3 id="predicates-every-some">Predicates: every, some,…</h3>
<p><code>every, notevery (test, sequence)</code>: return nil or t, respectively, as
soon as one test on any set of the corresponding elements of sequences
returns nil.</p>
<pre><code class="language-lisp">(defparameter foo '(1 2 3))
(every #'evenp foo)
;; =&gt; NIL
(some #'evenp foo)
;; =&gt; T
</code></pre>
<p>with a list of strings:</p>
<pre><code class="language-lisp">(defparameter str '("foo" "bar" "team"))
(every #'stringp str)
;; =&gt; T
(some #'(lambda (it) (= 3 (length it))) str)
;; =&gt; T
</code></pre>
<p><code>some</code>, <code>notany</code> <em>(test, sequence)</em>: return either the value of the test, or nil.</p>
<h3 id="functions">Functions</h3>
<p>See also sequence functions defined in
<a href="https://common-lisp.net/project/alexandria/draft/alexandria.html#Sequences">Alexandria</a>:
<code>starts-with</code>, <code>ends-with</code>, <code>ends-with-subseq</code>, <code>length=</code>, <code>emptyp</code>,…</p>
<h4 id="length-sequence">length (sequence)</h4>
<h4 id="elt-sequence-index---find-by-index">elt (sequence, index) - find by index</h4>
<p>beware, here the sequence comes first.</p>
<h4 id="count-foo-sequence">count (foo sequence)</h4>
<p>Return the number of elements in sequence that match <em>foo</em>.</p>
<p>Additional paramaters: <code>:from-end</code>, <code>:start</code>, <code>:end</code>.</p>
<p>See also <code>count-if</code>, <code>count-not</code> <em>(test-function sequence)</em>.</p>
<h4 id="subseq-sequence-start-end">subseq (sequence start, [end])</h4>
<pre><code class="language-lisp">(subseq (list 1 2 3) 0)
;; (1 2 3)
(subseq (list 1 2 3) 1 2)
;; (2)
</code></pre>
<p>However, watch out if the <code>end</code> is larger than the list:</p>
<pre><code class="language-lisp">(subseq (list 1 2 3) 0 99)
;; =&gt; Error: the bounding indices 0 and 99 are bad for a sequence of length 3.
</code></pre>
<p>To this end, use <code>alexandria-2:subseq*</code>:</p>
<pre><code class="language-lisp">(alexandria-2:subseq* (list 1 2 3) 0 99)
;; (1 2 3)
</code></pre>
<p><code>subseq</code> is “setf”able, but only works if the new sequence has the same
length of the one to replace.</p>
<h4 id="sort-stable-sort-sequence-test--key-function">sort, stable-sort (sequence, test [, key function])</h4>
<p>These sort functions are destructive, so one may prefer to copy the sequence with <code>copy-seq</code> before sorting:</p>
<pre><code class="language-lisp">(sort (copy-seq seq) :test #'string&lt;)
</code></pre>
<p>Unlike <code>sort</code>, <code>stable-sort</code> guarantees to keep the order of the argument.
In theory, the result of this:</p>
<pre><code class="language-lisp">(sort '((1 :a) (1 :b)) #'&lt; :key #'first)
</code></pre>
<p>could be either <code>((1 :A) (1 :B))</code>, either <code>((1 :B) (1 :A))</code>. On my tests, the order is preserved, but the standard does not guarantee it.</p>
<h4 id="find-position-foo-sequence---get-index">find, position (foo, sequence) - get index</h4>
<p>also <code>find-if</code>, <code>find-if-not</code>, <code>position-if</code>, <code>position-if-not</code> <em>(test
sequence)</em>. See <code>:key</code> and <code>:test</code> parameters.</p>
<pre><code class="language-lisp">(find 20 '(10 20 30))
;; 20
(position 20 '(10 20 30))
;; 1
</code></pre>
<h4 id="search-and-mismatch-sequence-a-sequence-b">search and mismatch (sequence-a, sequence-b)</h4>
<p><code>search</code> searches in sequence-b for a subsequence that matches sequence-a. It returns the
<em>position</em> in sequence-b, or NIL. It has the <code>from-end</code>, <code>end1</code>, <code>end2</code> and the usual <code>test</code> and <code>key</code>
parameters.</p>
<pre><code class="language-lisp">(search '(20 30) '(10 20 30 40))
;; 1
(search '("b" "c") '("a" "b" "c"))
;; NIL
(search '("b" "c") '("a" "b" "c") :test #'equal)
;; 1
(search "bc" "abc")
;; 1
</code></pre>
<p><code>mismatch</code> returns the position where the two sequences start to differ:</p>
<pre><code class="language-lisp">(mismatch '(10 20 99) '(10 20 30))
;; 2
(mismatch "hellolisper" "helloworld")
;; 5
(mismatch "same" "same")
;; NIL
(mismatch "foo" "bar")
;; 0
</code></pre>
<h4 id="substitute-nsubstituteifif-not">substitute, nsubstitute[if,if-not]</h4>
<p>Return a sequence of the same kind as <code>sequence</code> with the same elements,
except that all elements equal to <code>old</code> are replaced with <code>new</code>.</p>
<pre><code class="language-lisp">(substitute #\o #\x "hellx") ;; =&gt; "hello"
(substitute :a :x '(:a :x :x)) ;; =&gt; (:A :A :A)
(substitute "a" "x" '("a" "x" "x") :test #'string=) ;; =&gt; ("a" "a" "a")
</code></pre>
<h4 id="sort-stable-sort-merge">sort, stable-sort, merge</h4>
<p>(see above)</p>
<h4 id="replace-sequence-a-sequence-b-key-start1-end1">replace (sequence-a, sequence-b, &amp;key start1, end1)</h4>
<p>Destructively replace elements of sequence-a with elements of
sequence-b.</p>
<p>The full signature is:</p>
<pre><code class="language-lisp">(replace sequence1 sequence2 &amp;rest args &amp;key (start1 0) (end1 nil) (start2 0)
(end2 nil))
</code></pre>
<p>Elements are copied to the subseqeuence bounded by START1 and END1,
from the subsequence bounded by START2 and END2. If these subsequences
are not of the same length, then the shorter length determines how
many elements are copied.</p>
<pre><code class="language-lisp">(replace "xxx" "foo")
"foo"
(replace "xxx" "foo" :start1 1)
"xfo"
(replace "xxx" "foo" :start1 1 :start2 1)
"xoo"
(replace "xxx" "foo" :start1 1 :start2 1 :end2 2)
"xox"
</code></pre>
<h4 id="remove-delete-foo-sequence">remove, delete (foo sequence)</h4>
<p>Make a copy of sequence without elements matching foo. Has
<code>:start/end</code>, <code>:key</code> and <code>:count</code> parameters.</p>
<p><code>delete</code> is the recycling version of <code>remove</code>.</p>
<pre><code class="language-lisp">(remove "foo" '("foo" "bar" "foo") :test 'equal)
;; =&gt; ("bar")
</code></pre>
<p>see also <code>remove-if[-not]</code> below.</p>
<h4 id="remove-duplicates-delete-duplicates-sequence">remove-duplicates, delete-duplicates (sequence)</h4>
<p><a href="http://clhs.lisp.se/Body/f_rm_dup.htm">remove-duplicates</a> returns a
new sequence with uniq elements. <code>delete-duplicates</code> may modify the
original sequence.</p>
<p><code>remove-duplicates</code> accepts the following, usual arguments: <code>from-end
test test-not start end key</code>.</p>
<pre><code class="language-lisp">(remove-duplicates '(:foo :foo :bar))
(:FOO :BAR)
(remove-duplicates '("foo" "foo" "bar"))
("foo" "foo" "bar")
(remove-duplicates '("foo" "foo" "bar") :test #'string-equal)
("foo" "bar")
</code></pre>
<h3 id="mapping-map-mapcar-remove-if-not">mapping (map, mapcar, remove-if[-not],…)</h3>
<p>If youre used to map and filter in other languages, you probably want
<code>mapcar</code>. But it only works on lists, so to iterate on vectors (and
produce either a vector or a list, use <code>(map 'list function vector)</code>.</p>
<p>mapcar also accepts multiple lists with <code>&amp;rest more-seqs</code>. The
mapping stops as soon as the shortest sequence runs out.</p>
<p><code>map</code> takes the output-type as first argument (<code>'list</code>, <code>'vector</code> or
<code>'string</code>):</p>
<pre><code class="language-lisp">(defparameter foo '(1 2 3))
(map 'list (lambda (it) (* 10 it)) foo)
</code></pre>
<p><code>reduce</code> <em>(function, sequence)</em>. Special parameter: <code>:initial-value</code>.</p>
<pre><code class="language-lisp">(reduce '- '(1 2 3 4))
;; =&gt; -8
(reduce '- '(1 2 3 4) :initial-value 100)
;; =&gt; 90
</code></pre>
<p><strong>Filter</strong> is here called <code>remove-if-not</code>.</p>
<h3 id="flatten-a-list-alexandria">Flatten a list (Alexandria)</h3>
<p>With
<a href="https://common-lisp.net/project/alexandria/draft/alexandria.html">Alexandria</a>,
we have the <code>flatten</code> function.</p>
<h3 id="creating-lists-with-variables">Creating lists with variables</h3>
<p>Thats one use of the <code>backquote</code>:</p>
<pre><code class="language-lisp">(defparameter *var* "bar")
;; First try:
'("foo" *var* "baz") ;; no backquote
;; =&gt; ("foo" *VAR* "baz") ;; nope
</code></pre>
<p>Second try, with backquote interpolation:</p>
<pre><code class="language-lisp">`("foo" ,*var* "baz") ;; backquote, comma
;; =&gt; ("foo" "bar" "baz") ;; good
</code></pre>
<p>The backquote first warns well do interpolation, the comma introduces
the value of the variable.</p>
<p>If our variable is a list:</p>
<pre><code class="language-lisp">(defparameter *var* '("bar" "baz"))
;; First try:
`("foo" ,*var*)
;; =&gt; ("foo" ("bar" "baz")) ;; nested list
`("foo" ,@*var*) ;; backquote, comma-@ to
;; =&gt; ("foo" "bar" "baz")
</code></pre>
<p>E. Weitz warns that “objects generated this way will very likely share
structure (see Recipe 2-7)”.</p>
<h3 id="comparing-lists">Comparing lists</h3>
<p>We can use sets functions.</p>
<h2 id="set">Set</h2>
<p>We show below how to use set operations on lists.</p>
<p>A set doesnt contain twice the same element and is unordered.</p>
<p>Most of these functions have recycling (modifying) counterparts, starting with “n”: <code>nintersection</code>,… They all accept the usual <code>:key</code> and <code>:test</code> arguments, so use the test <code>#'string=</code> or <code>#'equal</code> if you are working with strings.</p>
<p>For more, see functions in
<a href="https://common-lisp.net/project/alexandria/draft/alexandria.html#Conses">Alexandria</a>:
<code>setp</code>, <code>set-equal</code>,… and the FSet library, shown in the next section.</p>
<h3 id="intersection-of-lists"><code>intersection</code> of lists</h3>
<p>What elements are both in list-a and list-b ?</p>
<pre><code class="language-lisp">(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; =&gt; (2 0)
</code></pre>
<h3 id="remove-the-elements-of-list-b-from-list-a-set-difference">Remove the elements of list-b from list-a (<code>set-difference</code>)</h3>
<pre><code class="language-lisp">(set-difference list-a list-b)
;; =&gt; (3 1)
(set-difference list-b list-a)
;; =&gt; (4)
</code></pre>
<h3 id="join-two-lists-with-uniq-elements-union">Join two lists with uniq elements (<code>union</code>)</h3>
<pre><code class="language-lisp">(union list-a list-b)
;; =&gt; (3 1 0 2 4) ;; order can be different in your lisp
</code></pre>
<h3 id="remove-elements-that-are-in-both-lists-set-exclusive-or">Remove elements that are in both lists (<code>set-exclusive-or</code>)</h3>
<pre><code class="language-lisp">(set-exclusive-or list-a list-b)
;; =&gt; (4 3 1)
</code></pre>
<h3 id="add-an-element-to-a-set-adjoin">Add an element to a set (<code>adjoin</code>)</h3>
<p>A new set is returned, the original set is not modified.</p>
<pre><code class="language-lisp">(adjoin 3 list-a)
;; =&gt; (0 1 2 3) ;; &lt;-- nothing was changed, 3 was already there.
(adjoin 5 list-a)
;; =&gt; (5 0 1 2 3) ;; &lt;-- element added in front.
list-a
;; =&gt; (0 1 2 3) ;; &lt;-- original list unmodified.
</code></pre>
<h3 id="check-if-this-is-a-subset-subsetp">Check if this is a subset (<code>subsetp</code>)</h3>
<pre><code class="language-lisp">(subsetp '(1 2 3) list-a)
;; =&gt; T
(subsetp '(1 1 1) list-a)
;; =&gt; T
(subsetp '(3 2 1) list-a)
;; =&gt; T
(subsetp '(0 3) list-a)
;; =&gt; T
</code></pre>
<h2 id="fset---immutable-data-structure">Fset - immutable data structure</h2>
<p>You may want to have a look at the
<a href="https://common-lisp.net/project/fset/Site/FSet-Tutorial.html">FSet</a>
library (in Quicklisp).</p>
<h2 id="arrays-and-vectors">Arrays and vectors</h2>
<p><strong>Arrays</strong> have constant-time access characteristics.</p>
<p>They can be fixed or adjustable. A <em>simple array</em> is neither displaced
(using <code>:displaced-to</code>, to point to another array) nor adjustable
(<code>:adjust-array</code>), nor does it have a fill pointer (<code>fill-pointer</code>,
that moves when we add or remove elements).</p>
<p>A <strong>vector</strong> is an array with rank 1 (of one dimension). It is also a
<em>sequence</em> (see above).</p>
<p>A <em>simple vector</em> is a simple array that is also not specialized (it
doesnt use <code>:element-type</code> to set the types of the elements).</p>
<h3 id="create-an-array-one-or-many-dimensions">Create an array, one or many dimensions</h3>
<p><code>make-array</code> <em>(sizes-list :adjustable bool)</em></p>
<p><code>adjust-array</code> <em>(array, sizes-list, :element-type, :initial-element)</em></p>
<h3 id="access-aref-array-i-j-">Access: aref (array i [j …])</h3>
<p><code>aref</code> <em>(array i j k …)</em> or <code>row-major-aref</code> <em>(array i)</em> equivalent to
<code>(aref i i i …)</code>.</p>
<p>The result is <code>setf</code>able.</p>
<pre><code class="language-lisp">(defparameter myarray (make-array '(2 2 2) :initial-element 1))
myarray
;; =&gt; #3A(((1 1) (1 1)) ((1 1) (1 1)))
(aref myarray 0 0 0)
;; =&gt; 1
(setf (aref myarray 0 0 0) 9)
;; =&gt; 9
(row-major-aref myarray 0)
;; =&gt; 9
</code></pre>
<h3 id="sizes">Sizes</h3>
<p><code>array-total-size</code> <em>(array)</em>: how many elements will fit in the array ?</p>
<p><code>array-dimensions</code> <em>(array)</em>: list containing the length of the arrays dimensions.</p>
<p><code>array-dimension</code> <em>(array i)</em>: length of the <em>i</em>th dimension.</p>
<p><code>array-rank</code> number of dimensions of the array.</p>
<pre><code class="language-lisp">(defparameter myarray (make-array '(2 2 2)))
;; =&gt; MYARRAY
myarray
;; =&gt; #3A(((0 0) (0 0)) ((0 0) (0 0)))
(array-rank myarray)
;; =&gt; 3
(array-dimensions myarray)
;; =&gt; (2 2 2)
(array-dimension myarray 0)
;; =&gt; 2
(array-total-size myarray)
;; =&gt; 8
</code></pre>
<h3 id="vectors">Vectors</h3>
<p>Create with <code>vector</code> or the reader macro <code>#()</code>. It returns a <em>simple
vector.</em></p>
<pre><code class="language-lisp">(vector 1 2 3)
;; =&gt; #(1 2 3)
#(1 2 3)
;; =&gt; #(1 2 3)
</code></pre>
<p><code>vector-push</code> <em>(foo vector)</em>: replace the vector element pointed to by
the fill pointer by foo. Can be destructive.</p>
<p><code>vector-push-extend</code> <em>(foo vector [extension-num])</em>t</p>
<p><code>vector-pop</code> <em>(vector)</em>: return the element of vector its fill pointer
points to.</p>
<p><code>fill-pointer</code> <em>(vector)</em>. <code>setf</code>able.</p>
<p>and see also the <em>sequence</em> functions.</p>
<h3 id="transforming-a-vector-to-a-list">Transforming a vector to a list.</h3>
<p>If youre mapping over it, see the <code>map</code> function whose first parameter
is the result type.</p>
<p>Or use <code>(coerce vector 'list)</code>.</p>
<h2 id="hash-table">Hash Table</h2>
<p>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.</p>
<p>Alists can be used sometimes differently though:</p>
<ul>
<li>they can be ordered</li>
<li>we can push cons cells that have the same key, remove the one in
front and we have a stack</li>
<li>they have a human-readable printed representation</li>
<li>they can be easily (de)serialized</li>
<li>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).</li>
</ul>
<p><a name="create"></a></p>
<h3 id="creating-a-hash-table">Creating a Hash Table</h3>
<p>Hash Tables are created using the function
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_mk_has.htm"><code>make-hash-table</code></a>. It
has no required argument. Its most used optional keyword argument is
<code>:test</code>, specifying the function used to test the equality of keys.</p>
<div class="info-box info">
<strong>Note:</strong> see shorter notations in the <a href="https://github.com/ruricolist/serapeum/">Serapeum</a> or <a href="https://github.com/vseloved/rutils">Rutils</a> libraries. For example, Serapeum has <code>dict</code>, and Rutils a <code>#h</code> reader macro.
</div>
<p><a name="add"></a></p>
<h3 id="adding-an-element-to-a-hash-table">Adding an Element to a Hash Table</h3>
<p>If you want to add an element to a hash table, you can use <code>gethash</code>,
the function to retrieve elements from the hash table, in conjunction
with
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_setf_.htm"><code>setf</code></a>.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (setf (gethash 'one-entry *my-hash*) "one")
"one"
CL-USER&gt; (setf (gethash 'another-entry *my-hash*) 2/4)
1/2
CL-USER&gt; (gethash 'one-entry *my-hash*)
"one"
T
CL-USER&gt; (gethash 'another-entry *my-hash*)
1/2
T
</code></pre>
<p>With Serapeums <code>dict</code>, we can create a hash-table and add elements to
it in one go:</p>
<pre><code class="language-lisp">(defparameter *my-hash* (dict :one-entry "one" :another-entry 2/4))
;; =&gt;
(dict
:ONE-ENTRY "one"
:ANOTHER-ENTRY 1/2
)
</code></pre>
<p><a name="get"></a></p>
<h3 id="getting-a-value-from-a-hash-table">Getting a value from a Hash Table</h3>
<p>The function
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_gethas.htm"><code>gethash</code></a>
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 <code>nil</code>
if not found), and a boolean indicating whether the key was found in
the table. That second value is necessary since <code>nil</code> is a valid value
in a key-value pair, so getting <code>nil</code> as first value from <code>gethash</code>
does not necessarily mean that the key was not found in the table.</p>
<h4 id="getting-a-key-that-does-not-exist-with-a-default-value">Getting a key that does not exist with a default value</h4>
<p><code>gethash</code> has an optional third argument:</p>
<pre><code class="language-lisp">(gethash 'bar *my-hash* "default-bar")
;; =&gt; "default-bar"
;; NIL
</code></pre>
<h4 id="getting-all-keys-or-all-values-of-a-hash-table">Getting all keys or all values of a hash table</h4>
<p>The
<a href="https://common-lisp.net/project/alexandria/draft/alexandria.html">Alexandria</a>
library (in Quicklisp) has the functions <code>hash-table-keys</code> and
<code>hash-table-values</code> for that.</p>
<pre><code class="language-lisp">(ql:quickload "alexandria")
;; […]
(alexandria:hash-table-keys *my-hash*)
;; =&gt; (BAR)
</code></pre>
<p><a name="test"></a></p>
<h3 id="testing-for-the-presence-of-a-key-in-a-hash-table">Testing for the Presence of a Key in a Hash Table</h3>
<p>The first value returned by <code>gethash</code> is the object in the hash table
thats associated with the key you provided as an argument to
<code>gethash</code> or <code>nil</code> if no value exists for this key. This value can act
as a
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_g.htm#generalized_boolean&quot;&gt;generalized
boolean">generalized boolean</a> if you want to test for the presence of keys.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (setf (gethash 'one-entry *my-hash*) "one")
"one"
CL-USER&gt; (if (gethash 'one-entry *my-hash*)
"Key exists"
"Key does not exist")
"Key exists"
CL-USER&gt; (if (gethash 'another-entry *my-hash*)
"Key exists"
"Key does not exist")
"Key does not exist"
</code></pre>
<p>But note that this does <em>not</em> work if <code>nil</code> is amongst the values that
you want to store in the hash.</p>
<pre><code class="language-lisp">CL-USER&gt; (setf (gethash 'another-entry *my-hash*) nil)
NIL
CL-USER&gt; (if (gethash 'another-entry *my-hash*)
"Key exists"
"Key does not exist")
"Key does not exist"
</code></pre>
<p>In this case youll have to check the <em>second</em> return value of <code>gethash</code> which will always return <code>nil</code> if no value is found and T otherwise.</p>
<pre><code class="language-lisp">CL-USER&gt; (if (nth-value 1 (gethash 'another-entry *my-hash*))
"Key exists"
"Key does not exist")
"Key exists"
CL-USER&gt; (if (nth-value 1 (gethash 'no-entry *my-hash*))
"Key exists"
"Key does not exist")
"Key does not exist"
</code></pre>
<p><a name="del"></a></p>
<h3 id="deleting-from-a-hash-table">Deleting from a Hash Table</h3>
<p>Use
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_remhas.htm"><code>remhash</code></a>
to delete a hash entry. Both the key and its associated value will be
removed from the hash table. <code>remhash</code> returns T if there was such an
entry, <code>nil</code> otherwise.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (setf (gethash 'first-key *my-hash*) 'one)
ONE
CL-USER&gt; (gethash 'first-key *my-hash*)
ONE
T
CL-USER&gt; (remhash 'first-key *my-hash*)
T
CL-USER&gt; (gethash 'first-key *my-hash*)
NIL
NIL
CL-USER&gt; (gethash 'no-entry *my-hash*)
NIL
NIL
CL-USER&gt; (remhash 'no-entry *my-hash*)
NIL
CL-USER&gt; (gethash 'no-entry *my-hash*)
NIL
NIL
</code></pre>
<p><a name="del-tab"></a></p>
<h3 id="deleting-a-hash-table">Deleting a Hash Table</h3>
<p>Use
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_clrhas.htm"><code>clrhash</code></a>
to delete a hash table. This will remove all of the data from the hash table and return the deleted table.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (setf (gethash 'first-key *my-hash*) 'one)
ONE
CL-USER&gt; (setf (gethash 'second-key *my-hash*) 'two)
TWO
CL-USER&gt; *my-hash*
#&lt;hash-table :TEST eql :COUNT 2 {10097BF4E3}&gt;
CL-USER&gt; (clrhash *my-hash*)
#&lt;hash-table :TEST eql :COUNT 0 {10097BF4E3}&gt;
CL-USER&gt; (gethash 'first-key *my-hash*)
NIL
NIL
CL-USER&gt; (gethash 'second-key *my-hash*)
NIL
NIL
</code></pre>
<p><a name="traverse"></a></p>
<h3 id="traversing-a-hash-table">Traversing a Hash Table</h3>
<p>If you want to perform an action on each entry (i.e., each key-value
pair) in a hash table, you have several options:</p>
<p>You can use
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_maphas.htm"><code>maphash</code></a>
which iterates over all entries in the hash table. Its first argument
must be a function which accepts <em>two</em> arguments, the key and the
value of each entry. Note that due to the nature of hash tables you
<em>cant</em> control the order in which the entries are provided by
<code>maphash</code> (or other traversing constructs). <code>maphash</code> always returns
<code>nil</code>.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (setf (gethash 'first-key *my-hash*) 'one)
ONE
CL-USER&gt; (setf (gethash 'second-key *my-hash*) 'two)
TWO
CL-USER&gt; (setf (gethash 'third-key *my-hash*) nil)
NIL
CL-USER&gt; (setf (gethash nil *my-hash*) 'nil-value)
NIL-VALUE
CL-USER&gt; (defun print-hash-entry (key value)
(format t "The value associated with the key ~S is ~S~%" key value))
PRINT-HASH-ENTRY
CL-USER&gt; (maphash #'print-hash-entry *my-hash*)
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
</code></pre>
<p>You can also use
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_w_hash.htm"><code>with-hash-table-iterator</code></a>,
a macro which turns (via
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm"><code>macrolet</code></a>)
its first argument into an iterator that on each invocation returns
three values per hash table entry - a generalized boolean thats 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 -
<code>nil</code>.</p>
<pre><code class="language-lisp">;;; same hash-table as above
CL-USER&gt; (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
</code></pre>
<p>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 <code>with-hash-table-iterator</code>
form such as by returning some closure over the invocation form.”</p>
<p>And theres always <a href="http://www.lispworks.com/documentation/HyperSpec/Body/06_a.htm"><code>loop</code></a>:</p>
<pre><code class="language-lisp">;;; same hash-table as above
CL-USER&gt; (loop for key being the hash-keys of *my-hash*
do (print key))
FIRST-KEY
SECOND-KEY
THIRD-KEY
NIL
NIL
CL-USER&gt; (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&gt; (loop for value being the hash-values of *my-hash*
do (print value))
ONE
TWO
NIL
NIL-VALUE
NIL
CL-USER&gt; (loop for value being the hash-values of *my-hash*
using (hash-key key)
do (format t "~&amp;~A -&gt; ~A" key value))
FIRST-KEY -&gt; ONE
SECOND-KEY -&gt; TWO
THIRD-KEY -&gt; NIL
NIL -&gt; NIL-VALUE
NIL
</code></pre>
<h4 id="traversing-keys-or-values">Traversing keys or values</h4>
<p>To map over keys or values we can again rely on Alexandria with
<code>maphash-keys</code> and <code>maphash-values</code>.</p>
<p><a name="count"></a></p>
<h3 id="counting-the-entries-in-a-hash-table">Counting the Entries in a Hash Table</h3>
<p>No need to use your fingers - Common Lisp has a built-in function to
do it for you:
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_hash_1.htm"><code>hash-table-count</code></a>.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (hash-table-count *my-hash*)
0
CL-USER&gt; (setf (gethash 'first *my-hash*) 1)
1
CL-USER&gt; (setf (gethash 'second *my-hash*) 2)
2
CL-USER&gt; (setf (gethash 'third *my-hash*) 3)
3
CL-USER&gt; (hash-table-count *my-hash*)
3
CL-USER&gt; (setf (gethash 'second *my-hash*) 'two)
TWO
CL-USER&gt; (hash-table-count *my-hash*)
3
CL-USER&gt; (clrhash *my-hash*)
#&lt;EQL hash table, 0 entries {48205F35}&gt;
CL-USER&gt; (hash-table-count *my-hash*)
0
</code></pre>
<h3 id="printing-a-hash-table-readably">Printing a Hash Table readably</h3>
<p><strong>With print-object</strong> (non portable)</p>
<p>It is very tempting to use <code>print-object</code>. It works under several
implementations, but this method is actually not portable. The
standard doesnt permit to do so, so this is undefined behaviour.</p>
<pre><code class="language-lisp">(defmethod print-object ((object hash-table) stream)
(format stream "#HASH{~{~{(~a : ~a)~}~^ ~}}"
(loop for key being the hash-keys of object
using (hash-value value)
collect (list key value))))
;; WARNING:
;; redefining PRINT-OBJECT (#&lt;STRUCTURE-CLASS COMMON-LISP:HASH-TABLE&gt;
;; #&lt;SB-PCL:SYSTEM-CLASS COMMON-LISP:T&gt;) in DEFMETHOD
;; #&lt;STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (HASH-TABLE T) {1006A0D063}&gt;
</code></pre>
<p>and lets try it:</p>
<pre><code class="language-lisp">(let ((ht (make-hash-table)))
(setf (gethash :foo ht) :bar)
ht)
;; #HASH{(FOO : BAR)}
</code></pre>
<p><strong>With a custom function</strong> (portable way)</p>
<p>Heres a portable way.</p>
<p>This snippets prints the keys, values and the test function of a
hash-table, and uses <code>alexandria:alist-hash-table</code> to read it back in:</p>
<pre><code class="language-lisp">;; https://github.com/phoe/phoe-toolbox/blob/master/phoe-toolbox.lisp
(defun print-hash-table-readably (hash-table
&amp;optional (stream *standard-output*))
"Prints a hash table readably using ALEXANDRIA:ALIST-HASH-TABLE."
(let ((test (hash-table-test hash-table))
(*print-circle* t)
(*print-readably* t))
(format stream "#.(ALEXANDRIA:ALIST-HASH-TABLE '(~%")
(maphash (lambda (k v) (format stream " (~S . ~S)~%" k v)) hash-table)
(format stream " ) :TEST '~A)" test)
hash-table))
</code></pre>
<p>Example output:</p>
<pre><code>#.(ALEXANDRIA:ALIST-HASH-TABLE
'((ONE . 1))
:TEST 'EQL)
#&lt;HASH-TABLE :TEST EQL :COUNT 1 {10046D4863}&gt;
</code></pre>
<p>This output can be read back in to create a hash-table:</p>
<pre><code class="language-lisp">(read-from-string
(with-output-to-string (s)
(print-hash-table-readably
(alexandria:alist-hash-table
'((a . 1) (b . 2) (c . 3))) s)))
;; #&lt;HASH-TABLE :TEST EQL :COUNT 3 {1009592E23}&gt;
;; 83
</code></pre>
<p><strong>With Serapeum</strong> (readable and portable)</p>
<p>The <a href="https://github.com/ruricolist/serapeum/blob/master/REFERENCE.md#hash-tables">Serapeum library</a>
has the <code>dict</code> constructor, the function <code>pretty-print-hash-table</code> and
the <code>toggle-pretty-print-hash-table</code> switch, all which do <em>not</em> use
<code>print-object</code> under the hood.</p>
<pre><code class="language-lisp">CL-USER&gt; (serapeum:toggle-pretty-print-hash-table)
T
CL-USER&gt; (serapeum:dict :a 1 :b 2 :c 3)
(dict
:A 1
:B 2
:C 3
)
</code></pre>
<p>This printed representation can be read back in.</p>
<p><a name="size"></a></p>
<h3 id="thread-safe-hash-tables">Thread-safe Hash Tables</h3>
<p>The standard hash-table in Common Lisp is not thread-safe. That means
that simple access operations can be interrupted in the middle and
return a wrong result.</p>
<p>Implementations offer different solutions.</p>
<p>With <strong>SBCL</strong>, we can create thread-safe hash tables with the <code>:synchronized</code> keyword to <code>make-hash-table</code>: <a href="http://www.sbcl.org/manual/#Hash-Table-Extensions">http://www.sbcl.org/manual/#Hash-Table-Extensions</a>.</p>
<blockquote>
<p>If nil (the default), the hash-table may have multiple concurrent readers, but results are undefined if a thread writes to the hash-table concurrently with another reader or writer. If t, all concurrent accesses are safe, but note that <a href="http://www.lispworks.com/documentation/HyperSpec/Body/03_f.htm">clhs 3.6 (Traversal Rules and Side Effects)</a> remains in force. See also: sb-ext:with-locked-hash-table.</p>
</blockquote>
<pre><code class="language-lisp">(defparameter *my-hash* (make-hash-table :synchronized t))
</code></pre>
<p>But, operations that expand to two accesses, like the modify macros (<code>incf</code>) or this:</p>
<pre><code class="language-lisp">(setf (gethash :a *my-hash*) :new-value)
</code></pre>
<p>need to be wrapped around <code>sb-ext:with-locked-hash-table</code>:</p>
<blockquote>
<p>Limits concurrent accesses to HASH-TABLE for the duration of BODY. If HASH-TABLE is synchronized, BODY will execute with exclusive ownership of the table. If HASH-TABLE is not synchronized, BODY will execute with other WITH-LOCKED-HASH-TABLE bodies excluded exclusion of hash-table accesses not surrounded by WITH-LOCKED-HASH-TABLE is unspecified.</p>
</blockquote>
<pre><code class="language-lisp">(sb-ext:with-locked-hash-table (*my-hash*)
(setf (gethash :a *my-hash*) :new-value))
</code></pre>
<p>In <strong>LispWorks</strong>, hash-tables are thread-safe by default. But
likewise, there is no guarantee of atomicity <em>between</em> access
operations, so we can use
<a href="http://www.lispworks.com/documentation/lw71/LW/html/lw-144.htm#pgfId-900768">with-hash-table-locked</a>.</p>
<p>Ultimately, you might like what the <a href="https://mdbergmann.github.io/cl-gserver/index.html#toc-2-4-1-hash-table-agent"><strong>cl-gserver library</strong></a>
proposes. It offers helper functions around hash-tables and its
actors/agent system to allow thread-safety. They also maintain the
order of updates and reads.</p>
<h3 id="performance-issues-the-size-of-your-hash-table">Performance Issues: The Size of your Hash Table</h3>
<p>The <code>make-hash-table</code> function has a couple of optional parameters
which control the initial size of your hash table and how itll grow
if it needs to grow. This can be an important performance issue if
youre working with large hash tables. Heres an (admittedly not very
scientific) example with <a href="http://www.cons.org/cmucl">CMUCL</a> pre-18d on
Linux:</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER&gt; (hash-table-size *my-hash*)
65
CL-USER&gt; (hash-table-rehash-size *my-hash*)
1.5
CL-USER&gt; (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.27 seconds of real time
0.25 seconds of user run time
0.02 seconds of system run time
0 page faults and
8754768 bytes consed.
NIL
CL-USER&gt; (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.05 seconds of real time
0.05 seconds of user run time
0.0 seconds of system run time
0 page faults and
0 bytes consed.
NIL
</code></pre>
<p>The values for
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_hash_4.htm"><code>hash-table-size</code></a>
and
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_hash_2.htm"><code>hash-table-rehash-size</code></a>
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. Lets see how often we have to re-size the
hash until we reach the final size…</p>
<pre><code class="language-lisp">CL-USER&gt; (log (/ 100000 65) 1.5)
18.099062
CL-USER&gt; (let ((size 65)) (dotimes (n 20) (print (list n size)) (setq size (* 1.5 size))))
(0 65)
(1 97.5)
(2 146.25)
(3 219.375)
(4 329.0625)
(5 493.59375)
(6 740.3906)
(7 1110.5859)
(8 1665.8789)
(9 2498.8184)
(10 3748.2275)
(11 5622.3413)
(12 8433.512)
(13 12650.268)
(14 18975.402)
(15 28463.104)
(16 42694.656)
(17 64041.984)
(18 96062.98)
(19 144094.47)
NIL
</code></pre>
<p>The hash has to be re-sized 19 times until its 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.</p>
<p>Heres a faster way to do it:
If we know in advance how big our hash will be, we can start with the right size:</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table :size 100000))
*MY-HASH*
CL-USER&gt; (hash-table-size *my-hash*)
100000
CL-USER&gt; (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.04 seconds of real time
0.04 seconds of user run time
0.0 seconds of system run time
0 page faults and
0 bytes consed.
NIL
</code></pre>
<p>Thats obviously much faster. And there was no consing involved
because we didnt have to re-size at all. If we dont know the final
size in advance but can guess the growth behaviour of our hash table
we can also provide this value to <code>make-hash-table</code>. We can provide an
integer to specify absolute growth or a float to specify relative
growth.</p>
<pre><code class="language-lisp">CL-USER&gt; (defparameter *my-hash* (make-hash-table :rehash-size 100000))
*MY-HASH*
CL-USER&gt; (hash-table-size *my-hash*)
65
CL-USER&gt; (hash-table-rehash-size *my-hash*)
100000
CL-USER&gt; (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.07 seconds of real time
0.05 seconds of user run time
0.01 seconds of system run time
0 page faults and
2001360 bytes consed.
NIL
</code></pre>
<p>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.</p>
<p>Note that you can also specify the <code>rehash-threshold</code> while creating a
new hash table. One final remark: Your implementation is allowed to
<em>completely ignore</em> the values provided for <code>rehash-size</code> and
<code>rehash-threshold</code></p>
<h2 id="alist">Alist</h2>
<h3 id="definition">Definition</h3>
<p>An association list is a list of cons cells.</p>
<p>This simple example:</p>
<pre><code class="language-lisp">(defparameter *my-alist* (list (cons 'foo "foo")
(cons 'bar "bar")))
;; =&gt; ((FOO . "foo") (BAR . "bar"))
</code></pre>
<p>looks like this:</p>
<pre><code>[o|o]---[o|/]
| |
| [o|o]---"bar"
| |
| BAR
|
[o|o]---"foo"
|
FOO
</code></pre>
<h3 id="construction">Construction</h3>
<p>We can construct an alist like its representation:</p>
<pre><code class="language-lisp">(setf *my-alist* '((:foo . "foo")
(:bar . "bar")))
</code></pre>
<p>The constructor <code>pairlis</code> associates a list of keys and a list of values:</p>
<pre><code class="language-lisp">(pairlis '(:foo :bar)
'("foo" "bar"))
;; =&gt; ((:BAR . "bar") (:FOO . "foo"))
</code></pre>
<p>Alists are just lists, so you can have the same key multiple times in the same alist:</p>
<pre><code class="language-lisp">(setf *alist-with-duplicate-keys*
'((:a . 1)
(:a . 2)
(:b . 3)
(:a . 4)
(:c . 5)))
</code></pre>
<h3 id="access">Access</h3>
<p>To get a key, we have <code>assoc</code> (use <code>:test 'equal</code> when your keys are
strings, as usual). It returns the whole cons cell, so you may want to
use <code>cdr</code> or <code>second</code> to get the value or even better <code>assoc-value list key</code> from <code>Alexandria</code>.</p>
<pre><code class="language-lisp">(alexandria:assoc-value *my-alist* :foo)
;; it actually returns 2 values
;; "foo"
;; (:FOO . "FOO")
</code></pre>
<p>There is <code>assoc-if</code>, and <code>rassoc</code> to get a cons cell by its value.</p>
<p>If the alist has repeating (duplicate) keys, you can use <code>remove-if-not</code>, for example, to retrieve all of them.</p>
<pre><code class="language-lisp">(remove-if-not
#'(lambda (entry)
(eq :a entry))
*alist-with-duplicate-keys*
:key #'car)
</code></pre>
<h3 id="insert-and-remove-entries">Insert and remove entries</h3>
<p>To add a key, we <code>push</code> another cons cell:</p>
<pre><code class="language-lisp">(push (cons 'team "team") *my-alist*)
;; =&gt; ((TEAM . "team") (FOO . "foo") (BAR . "bar"))
</code></pre>
<p>We can use <code>pop</code> and other functions that operate on lists, like <code>remove</code>:</p>
<pre><code class="language-lisp">(remove :team *my-alist*)
;; =&gt; ((:TEAM . "team") (FOO . "foo") (BAR . "bar")) ;; didn't remove anything
(remove :team *my-alist* :key 'car)
;; =&gt; ((FOO . "foo") (BAR . "bar")) ;; returns a copy
</code></pre>
<p>Remove only one element with <code>:count</code>:</p>
<pre><code class="language-lisp">(push (cons 'bar "bar2") *my-alist*)
;; =&gt; ((BAR . "bar2") (TEAM . "team") (FOO . "foo") (BAR . "bar")) ;; twice the 'bar key
(remove 'bar *my-alist* :key 'car :count 1)
;; =&gt; ((TEAM . "team") (FOO . "foo") (BAR . "bar"))
;; because otherwise:
(remove 'bar *my-alist* :key 'car)
;; =&gt; ((TEAM . "team") (FOO . "foo")) ;; no more 'bar
</code></pre>
<h3 id="update-entries">Update entries</h3>
<p>Replace a value:</p>
<pre><code class="language-lisp">*my-alist*
;; =&gt; '((:FOO . "foo") (:BAR . "bar"))
(assoc :foo *my-alist*)
;; =&gt; (:FOO . "foo")
(setf (cdr (assoc :foo *my-alist*)) "new-value")
;; =&gt; "new-value"
*my-alist*
;; =&gt; '((:foo . "new-value") (:BAR . "bar"))
</code></pre>
<p>Replace a key:</p>
<pre><code class="language-lisp">*my-alist*
;; =&gt; '((:FOO . "foo") (:BAR . "bar")))
(setf (car (assoc :bar *my-alist*)) :new-key)
;; =&gt; :NEW-KEY
*my-alist*
;; =&gt; '((:FOO . "foo") (:NEW-KEY . "bar")))
</code></pre>
<p>In the
<a href="https://common-lisp.net/project/alexandria/draft/alexandria.html#Conses">Alexandria</a>
library, see more functions like <code>hash-table-alist</code>, <code>alist-plist</code>,…</p>
<h2 id="plist">Plist</h2>
<p>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 <code>:test</code>). More
precisely, it first has a cons cell whose <code>car</code> is the key, whose
<code>cdr</code> points to the following cons cell whose <code>car</code> is the
value.</p>
<p>For example this plist:</p>
<pre><code class="language-lisp">(defparameter my-plist (list 'foo "foo" 'bar "bar"))
</code></pre>
<p>looks like this:</p>
<pre><code>[o|o]---[o|o]---[o|o]---[o|/]
| | | |
FOO "foo" BAR "bar"
</code></pre>
<p>We access an element with <code>getf (list elt)</code> (it returns the value)
(the list comes as first element),</p>
<p>we remove an element with <code>remf</code>.</p>
<pre><code class="language-lisp">(defparameter my-plist (list 'foo "foo" 'bar "bar"))
;; =&gt; (FOO "foo" BAR "bar")
(setf (getf my-plist 'foo) "foo!!!")
;; =&gt; "foo!!!"
</code></pre>
<h2 id="structures">Structures</h2>
<p>Structures offer a way to store data in named slots. They support
single inheritance.</p>
<p>Classes provided by the Common Lisp Object System (CLOS) are more flexible however structures may offer better performance (see for example the SBCL manual).</p>
<h3 id="creation">Creation</h3>
<p>Use <code>defstruct</code>:</p>
<pre><code class="language-lisp">(defstruct person
id name age)
</code></pre>
<p>At creation slots are optional and default to <code>nil</code>.</p>
<p>To set a default value:</p>
<pre><code class="language-lisp">(defstruct person
id
(name "john doe")
age)
</code></pre>
<p>Also specify the type after the default value:</p>
<pre><code class="language-lisp">(defstruct person
id
(name "john doe" :type string)
age)
</code></pre>
<p>We create an instance with the generated constructor <code>make-</code> +
<code>&lt;structure-name&gt;</code>, so <code>make-person</code>:</p>
<pre><code class="language-lisp">(defparameter *me* (make-person))
*me*
#S(PERSON :ID NIL :NAME "john doe" :AGE NIL)
</code></pre>
<p>note that printed representations can be read back by the reader.</p>
<p>With a bad name type:</p>
<pre><code class="language-lisp">(defparameter *bad-name* (make-person :name 123))
</code></pre>
<pre><code>Invalid initialization argument:
:NAME
in call for class #&lt;STRUCTURE-CLASS PERSON&gt;.
[Condition of type SB-PCL::INITARG-ERROR]
</code></pre>
<p>We can set the structures constructor so as to create the structure
without using keyword arguments, which can be more convenient
sometimes. We give it a name and the order of the arguments:</p>
<pre><code class="language-lisp">(defstruct (person (:constructor create-person (id name age)))
id
name
age)
</code></pre>
<p>Our new constructor is <code>create-person</code>:</p>
<pre><code class="language-lisp">(create-person 1 "me" 7)
#S(PERSON :ID 1 :NAME "me" :AGE 7)
</code></pre>
<p>However, the default <code>make-person</code> does <em>not</em> work any more:</p>
<pre><code class="language-lisp">(make-person :name "me")
;; debugger:
obsolete structure error for a structure of type PERSON
[Condition of type SB-PCL::OBSOLETE-STRUCTURE]
</code></pre>
<h3 id="slot-access">Slot access</h3>
<p>We access the slots with accessors created by <code>&lt;name-of-the-struct&gt;-</code> + <code>slot-name</code>:</p>
<pre><code class="language-lisp">(person-name *me*)
;; "john doe"
</code></pre>
<p>we then also have <code>person-age</code> and <code>person-id</code>.</p>
<h3 id="setting">Setting</h3>
<p>Slots are <code>setf</code>-able:</p>
<pre><code class="language-lisp">(setf (person-name *me*) "Cookbook author")
(person-name *me*)
;; "Cookbook author"
</code></pre>
<h3 id="predicate">Predicate</h3>
<p>A predicate function is generated:</p>
<pre><code class="language-lisp">(person-p *me*)
T
</code></pre>
<h3 id="single-inheritance">Single inheritance</h3>
<p>Use single inheritance with the <code>:include &lt;struct&gt;</code> argument:</p>
<pre><code class="language-lisp">(defstruct (female (:include person))
(gender "female" :type string))
(make-female :name "Lilie")
;; #S(FEMALE :ID NIL :NAME "Lilie" :AGE NIL :GENDER "female")
</code></pre>
<p>Note that the CLOS object system is more powerful.</p>
<h3 id="limitations">Limitations</h3>
<p>After a change, instances are not updated.</p>
<p>If we try to add a slot (<code>email</code> below), we have the choice to lose
all instances, or to continue using the new definition of
<code>person</code>. But the effects of redefining a structure are undefined by
the standard, so it is best to re-compile and re-run the changed
code.</p>
<pre><code class="language-lisp">(defstruct person
id
(name "john doe" :type string)
age
email)
attempt to redefine the STRUCTURE-OBJECT class PERSON
incompatibly with the current definition
[Condition of type SIMPLE-ERROR]
Restarts:
0: [CONTINUE] Use the new definition of PERSON, invalidating already-loaded code and instances.
1: [RECKLESSLY-CONTINUE] Use the new definition of PERSON as if it were compatible, allowing old accessors to use new instances and allowing new accessors to use old instances.
2: [CLOBBER-IT] (deprecated synonym for RECKLESSLY-CONTINUE)
3: [RETRY] Retry SLIME REPL evaluation request.
4: [*ABORT] Return to SLIME's top level.
5: [ABORT] abort thread (#&lt;THREAD "repl-thread" RUNNING {1002A0FFA3}&gt;)
</code></pre>
<p>If we choose restart <code>0</code>, to use the new definition, we lose access to <code>*me*</code>:</p>
<pre><code class="language-lisp">*me*
obsolete structure error for a structure of type PERSON
[Condition of type SB-PCL::OBSOLETE-STRUCTURE]
</code></pre>
<p>There is also very little introspection.
Portable Common Lisp does not define ways of finding out defined super/sub-structures nor what slots a structure has.</p>
<p>The Common Lisp Object System (which came after into the language)
doesnt have such limitations. See the <a href="clos.html">CLOS section</a>.</p>
<ul>
<li><a href="http://www.lispworks.com/documentation/HyperSpec/Body/08_.htm">structures on the hyperspec</a></li>
<li>David B. Lamkins, <a href="http://www.communitypicks.com/r/lisp/s/17592186045679-successful-lisp-how-to-understand-and-use-common">“Successful Lisp, How to Understand and Use Common Lisp”</a>.</li>
</ul>
<h2 id="tree">Tree</h2>
<p><code>tree-equal</code>, <code>copy-tree</code>. They descend recursively into the car and
the cdr of the cons cells they visit.</p>
<h3 id="sycamore---purely-functional-weight-balanced-binary-trees">Sycamore - purely functional weight-balanced binary trees</h3>
<p><a href="https://github.com/ndantam/sycamore">https://github.com/ndantam/sycamore</a></p>
<p>Features:</p>
<ul>
<li>Fast, purely functional weight-balanced binary trees.
<ul>
<li>Leaf nodes are simple-vectors, greatly reducing tree height.</li>
</ul>
</li>
<li>Interfaces for tree Sets and Maps (dictionaries).</li>
<li><a href="http://en.wikipedia.org/wiki/Rope_(data_structure)">Ropes</a></li>
<li>Purely functional <a href="http://en.wikipedia.org/wiki/Pairing_heap">pairing heaps</a></li>
<li>Purely functional amortized queue.</li>
</ul>
<h2 id="controlling-how-much-of-data-to-print-print-length-print-level">Controlling how much of data to print (<code>*print-length*</code>, <code>*print-level*</code>)</h2>
<p>Use <code>*print-length*</code> and <code>*print-level*</code>.</p>
<p>They are both <code>nil</code> by default.</p>
<p>If you have a very big list, printing it on the REPL or in a
stacktrace can take a long time and bring your editor or even your
server down. Use <code>*print-length*</code> to choose the maximum of elements of
the list to print, and to show there is a rest with a <code>...</code>
placeholder:</p>
<pre><code class="language-lisp">(setf *print-length* 2)
(list :A :B :C :D :E)
;; (:A :B ...)
</code></pre>
<p>And if you have a very nested data structure, set <code>*print-level*</code> to
choose the depth to print:</p>
<pre><code class="language-lisp">(let ((*print-level* 2))
(print '(:a (:b (:c (:d :e))))))
;; (:A (:B #)) &lt;= *print-level* in action
;; (:A (:B (:C (:D :E)))) &lt;= the list is returned, the let binding is not in effect anymore.
</code></pre>
<p><code>*print-length*</code> will be applied at each level.</p>
<p>Reference: the <a href="http://clhs.lisp.se/Body/v_pr_lev.htm">HyperSpec</a>.</p>
<h2 id="appendix-a---generic-and-nested-access-of-alists-plists-hash-tables-and-clos-slots">Appendix A - generic and nested access of alists, plists, hash-tables and CLOS slots</h2>
<p>The solutions presented below might help you getting started, but keep
in mind that theyll have a performance impact and that error messages
will be less explicit.</p>
<ul>
<li>the <a href="https://github.com/AccelerationNet/access">access</a> library (battle tested, used by the Djula templating system) has a generic <code>(access my-var :elt)</code> (<a href="https://lisp-journey.gitlab.io/blog/generice-consistent-access-of-data-structures-dotted-path/">blog post</a>). It also has <code>accesses</code> (plural) to access and set nested values.</li>
<li><a href="https://github.com/vseloved/rutils">rutils</a> as a generic <code>generic-elt</code> or <code>?</code>,</li>
</ul>
<h2 id="appendix-b---accessing-nested-data-structures">Appendix B - accessing nested data structures</h2>
<p>Sometimes we work with nested data structures, and we might want an
easier way to access a nested element than intricated “getf” and
“assoc” and all. Also, we might want to just be returned a <code>nil</code> when
an intermediary key doesnt exist.</p>
<p>The <code>access</code> library given above provides this, with <code>(accesses var key1 key2…)</code>.</p>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/data-structures.md">data-structures.md</a>
</p>
</div>
<script type="text/javascript">
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
</script>
<div>
<footer class="footer">
<hr/>
&copy; 2002&ndash;2021 the Common Lisp Cookbook Project
</footer>
</div>
<div id="toc-btn">T<br>O<br>C</div>
</div>
<script text="javascript">
HighlightLisp.highlight_auto({className: null});
</script>
<script type="text/javascript">
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField && searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2&kf=-1&ko=1&q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
</script>
<script async defer data-domain="lispcookbook.github.io/cl-cookbook" src="https://plausible.io/js/plausible.js"></script>
</body>
</html>