1521 lines
46 KiB
HTML
1521 lines
46 KiB
HTML
![]() |
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta name="generator" content=
|
|||
|
"HTML Tidy for HTML5 for Linux version 5.2.0">
|
|||
|
<title>Loop, iteration, mapping</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> – Loop, iteration, mapping</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> – Loop, iteration, mapping</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 style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPMACROSPOWER" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover vindarel's Lisp course in videos with this September coupon.</a>
|
|||
|
<strong>
|
|||
|
Recently added: 18 videos on MACROS.
|
|||
|
</strong>
|
|||
|
<a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</a>.
|
|||
|
</p>
|
|||
|
<p class="announce-neutral">
|
|||
|
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
|
|||
|
</p>
|
|||
|
|
|||
|
|
|||
|
<div id="content"
|
|||
|
<!-- needs some text before the first heading -->
|
|||
|
|
|||
|
<h2 id="introduction-loop-iterate-for-mapcar-series">Introduction: loop, iterate, for, mapcar, series</h2>
|
|||
|
|
|||
|
<p><strong><a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/m_loop.htm">loop</a></strong>
|
|||
|
is the built-in macro for iteration.</p>
|
|||
|
|
|||
|
<p>Its simplest form is <code>(loop (print "hello"))</code>: this will print forever.</p>
|
|||
|
|
|||
|
<p>A simple iteration over a list is:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(1 2 3)
|
|||
|
do (print x))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>It prints what’s needed but returns <code>nil</code>.</p>
|
|||
|
|
|||
|
<p>If you want to return a list, use <code>collect</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(1 2 3)
|
|||
|
collect (* x 10))
|
|||
|
;; (10 20 30)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The Loop macro is different than most Lisp expressions in having a complex
|
|||
|
internal domain-specific language that doesn’t use s-expressions.
|
|||
|
So you need to read Loop expressions with half of your brain in Lisp mode, and
|
|||
|
the other half in Loop mode. You love it or you hate it.</p>
|
|||
|
|
|||
|
<p>Think of Loop expressions as having four parts: expressions that set up
|
|||
|
variables that will be iterated, expressions that conditionally terminate the
|
|||
|
iteration, expressions that do something on each iteration, and expressions that
|
|||
|
do something right before the Loop exits. In addition, Loop expressions can
|
|||
|
return a value. It is very rare to use all of these parts in a given Loop
|
|||
|
expression, but you can combine them in many ways.</p>
|
|||
|
|
|||
|
<p><strong><a href="https://common-lisp.net/project/iterate/doc/index.html">iterate</a></strong> is a
|
|||
|
popular iteration macro that aims at being simpler, “lispier” and more
|
|||
|
predictable than <code>loop</code>, besides being extensible. However it isn’t built-in,
|
|||
|
so you have to import it:</p>
|
|||
|
|
|||
|
<pre><code>(ql:quickload "iterate")
|
|||
|
(use-package :iterate)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Iterate looks like this:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for i from 1 to 5)
|
|||
|
(collect (* i i)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>(if you use loop and iterate in the same package, you might run into name conflicts)</p>
|
|||
|
|
|||
|
<p>Iterate also comes with <code>display-iterate-clauses</code> that can be quite handy:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(display-iterate-clauses '(for))
|
|||
|
;; FOR PREVIOUS &OPTIONAL INITIALLY BACK Previous value of a variable
|
|||
|
;; FOR FIRST THEN Set var on first, and then on subsequent iterations
|
|||
|
;; ...
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Much of the examples on this page that are valid for loop are also valid for iterate,
|
|||
|
with minor modifications.</p>
|
|||
|
|
|||
|
<p><strong><a href="https://github.com/Shinmera/for/">for</a></strong> is an extensible iteration
|
|||
|
macro that is often shorter than loop, that “unlike loop is extensible
|
|||
|
and sensible, and unlike iterate does not require code-walking and is
|
|||
|
easier to extend”.</p>
|
|||
|
|
|||
|
<p>It has the other advantage of having one construct that works for all
|
|||
|
data structures (lists, vectors, hash-tables…): in doubt, just use
|
|||
|
<code>for… over</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(for:for ((x over <your data structure>))
|
|||
|
(print …))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>You also have to quickload it:</p>
|
|||
|
|
|||
|
<pre><code>(ql:quickload "for")
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>We’ll also give examples with <strong><code>mapcar</code></strong> and <code>map</code>, and eventually
|
|||
|
with their friends <code>mapcon</code>, <code>mapcan</code>, <code>maplist</code>, <code>mapc</code> and <code>mapl</code>
|
|||
|
which E. Weitz categorizes very well in his “Common Lisp Recipes”,
|
|||
|
chap. 7. The one you are certainly accustomed to from other languages is
|
|||
|
<code>mapcar</code>: it takes a function, one or more lists as arguments,
|
|||
|
applies the function on each <em>element</em> of the lists one by one and
|
|||
|
returns a list of result.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(mapcar (lambda (it) (+ it 10)) '(1 2 3))
|
|||
|
(11 12 13)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>map</code> is generic, it accepts list and vectors as arguments, and
|
|||
|
expects the type for its result as first argument:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(map 'vector (lambda (it) (+ it 10)) '(1 2 3))
|
|||
|
;; #(11 12 13)
|
|||
|
(map 'list (lambda (it) (+ it 10)) #(1 2 3))
|
|||
|
;; (11 12 13)
|
|||
|
(map 'string (lambda (it) (code-char it)) '#(97 98 99))
|
|||
|
;; "abc"
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The other constructs have their advantages in some situations ;) They
|
|||
|
either process the <em>tails</em> of lists, or <em>concatenate</em> the return
|
|||
|
values, or don’t return anything. We’ll see some of them.</p>
|
|||
|
|
|||
|
<p>If you like <code>mapcar</code>, use it a lot, and would like a quicker and
|
|||
|
shorter way to write lambdas, then you might like one of those
|
|||
|
<a href="https://github.com/CodyReichert/awesome-cl#lambda-shorthands">lambda shorthand libraries</a>.</p>
|
|||
|
|
|||
|
<p>Here is an example with <a href="https://github.com/windymelt/cl-punch/">cl-punch</a>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(mapcar ^(* _ 10) '(1 2 3))
|
|||
|
;; (10 20 30)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>and voilà :) We won’t use this more in this recipe, but feel free to do.</p>
|
|||
|
|
|||
|
<p>Last but not least, you might like
|
|||
|
<strong><a href="http://series.sourceforge.net/">series</a></strong>,
|
|||
|
a library that describes itself as combining aspects of sequences,
|
|||
|
streams, and loops. Series expressions look like operations on
|
|||
|
sequences (= functional programming), but can achieve the same high level of efficiency as a
|
|||
|
loop. Series first appeared in “Common Lisp the Language”, in the
|
|||
|
appendix A (it nearly became part of the language). Series looks like
|
|||
|
this:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(mapping ((x (scan-range :from 1 :upto 5)))
|
|||
|
(* x x)))
|
|||
|
;; (1 4 9 16 25)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>series</code> is good, but its function names are different from what we
|
|||
|
find in functional languages today. You might like the <a href="https://github.com/cbeo/gtwiwtg">“Generators
|
|||
|
The Way I Want Them Generated”</a>
|
|||
|
library. It is a lazy sequences library, similar to <code>series</code> although
|
|||
|
younger and not as complete, with a “modern” API with words like <code>take</code>, <code>filter</code>,
|
|||
|
<code>for</code> or <code>fold</code>, and that is easy to use.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(range :from 20)
|
|||
|
;; #<GTWIWTG::GENERATOR! {1001A90CA3}>
|
|||
|
|
|||
|
(take 4 (range :from 20))
|
|||
|
;; (20 21 22 23)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>At the time of writing, GTWIWTG is licensed under the GPLv3.</p>
|
|||
|
|
|||
|
<h2 id="recipes">Recipes</h2>
|
|||
|
|
|||
|
<h3 id="looping-forever-return">Looping forever, return</h3>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop
|
|||
|
(print "hello"))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>return</code> can return a result:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i in '(1 2 3)
|
|||
|
when (> i 1)
|
|||
|
return i)
|
|||
|
2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="looping-a-fixed-number-of-times">Looping a fixed number of times</h3>
|
|||
|
|
|||
|
<h4 id="dotimes">dotimes</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(dotimes (n 3)
|
|||
|
(print n))
|
|||
|
;; =>
|
|||
|
;; 0
|
|||
|
;; 1
|
|||
|
;; 2
|
|||
|
;; NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Here <code>dotimes</code> returns <code>nil</code>. There are two ways to return a value. First, you can set a result form in the lambda list:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(dotimes (n 3 :done)
|
|||
|
;; ^^^^^ result form. It can be a s-expression.
|
|||
|
(print n))
|
|||
|
;; =>
|
|||
|
;; 0
|
|||
|
;; 1
|
|||
|
;; 2
|
|||
|
;; :DONE
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Or you can use <code>return</code> with return values:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(dotimes (i 3)
|
|||
|
(if (> i 1)
|
|||
|
(return :early-exit!)
|
|||
|
(print i)))
|
|||
|
;; =>
|
|||
|
;; 0
|
|||
|
;; 1
|
|||
|
;; :EARLY-EXIT!
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="loop-repeat">loop… repeat</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop repeat 10
|
|||
|
do (format t "Hello!~%"))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>This prints 10 times “hello” and returns <code>nil</code>.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop repeat 10 collect (random 10))
|
|||
|
;; (5 1 3 5 4 0 7 4 9 1)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>with <code>collect</code>, this returns a list.</p>
|
|||
|
|
|||
|
<h4 id="series">Series</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((n (scan-range :below 10)))
|
|||
|
(print n))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="looping-an-infinite-number-of-times-cycling-over-a-circular-list">Looping an infinite number of times, cycling over a circular list</h3>
|
|||
|
|
|||
|
<p>First, as shown above, we can simply use <code>(loop ...)</code> to loop
|
|||
|
infinitely. Here we show how to loop on a list forever.</p>
|
|||
|
|
|||
|
<p>We can build an infinite list by setting its last element to the list itself:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop with list-a = '(1 2 3)
|
|||
|
with infinite-list = (setf (cdr (last list-a)) list-a)
|
|||
|
for item in infinite-list
|
|||
|
repeat 8
|
|||
|
collect item)
|
|||
|
;; (1 2 3 1 2 3 1 2)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Illustration: <code>(last '(1 2 3))</code> is <code>(3)</code>, a list, or rather a cons cell, whose <code>car</code> is 3 and <code>cdr</code> is NIL. See the <a href="data-structures.html">data-structures chapter</a> for a reminder. This is the representation of <code>(list 3)</code>:</p>
|
|||
|
|
|||
|
<pre><code>[o|/]
|
|||
|
|
|
|||
|
3
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The representation of <code>(list 1 2 3)</code>:</p>
|
|||
|
|
|||
|
<pre><code>[o|o]---[o|o]---[o|/]
|
|||
|
| | |
|
|||
|
1 2 3
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>By setting the <code>cdr</code> of the last element to the list itself, we make it recur on itself.</p>
|
|||
|
|
|||
|
<p>A notation shortcut is possible with the <code>#=</code> syntax:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(defparameter *list-a* '#1=(1 2 3 . #1#))
|
|||
|
(setf *print-circle* t) ;; don't print circular lists forever
|
|||
|
*list-a*
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>If you need to alternate only between two values, use <code>for … then</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop repeat 4
|
|||
|
for up = t then (not up)
|
|||
|
do (print up))
|
|||
|
T
|
|||
|
NIL
|
|||
|
T
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="iterates-for-loop">Iterate’s for loop</h3>
|
|||
|
|
|||
|
<p>For lists and vectors:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for item in '(1 2 3))
|
|||
|
(print item))
|
|||
|
(iter (for i in-vector #(1 2 3))
|
|||
|
(print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>or, a generalized iteration clause for lists and vectors, use
|
|||
|
<code>in-sequence</code> (you’ll pay a speed penalty).</p>
|
|||
|
|
|||
|
<p>Looping over a hash-table is also straightforward:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(let ((h (let ((h (make-hash-table)))
|
|||
|
(setf (gethash 'a h) 1)
|
|||
|
(setf (gethash 'b h) 2)
|
|||
|
h)))
|
|||
|
(iter (for (k v) in-hashtable h)
|
|||
|
(print k)))
|
|||
|
;; b
|
|||
|
;; a
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>In fact, take a look <a href="https://common-lisp.net/project/iterate/doc/Sequence-Iteration.html">here</a>,
|
|||
|
or <code>(display-iterate-clauses '(for))</code> to know about iterating over</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>symbols in-package</li>
|
|||
|
<li>forms - or lines, or whatever-you-wish - in-file, or in-stream</li>
|
|||
|
<li>elements in-sequence - sequences can be vectors or lists</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="looping-over-a-list">Looping over a list</h3>
|
|||
|
|
|||
|
<h4 id="dolist">dolist</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(dolist (item '(1 2 3))
|
|||
|
(print item))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>dolist</code> returns <code>nil</code>.</p>
|
|||
|
|
|||
|
<h4 id="loop">loop</h4>
|
|||
|
|
|||
|
<p>with <code>in</code>, no surprises:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(a b c)
|
|||
|
do (print x))
|
|||
|
;; A
|
|||
|
;; B
|
|||
|
;; C
|
|||
|
;; NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(a b c)
|
|||
|
collect x)
|
|||
|
;; (A B C)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>With <code>on</code>, we loop over the cdr of the list:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i on '(1 2 3) do (print i))
|
|||
|
;; (1 2 3)
|
|||
|
;; (2 3)
|
|||
|
;; (3)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="mapcar">mapcar</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(mapcar (lambda (x)
|
|||
|
(print (* x 10)))
|
|||
|
'(1 2 3))
|
|||
|
10
|
|||
|
20
|
|||
|
30
|
|||
|
(10 20 30)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>mapcar</code> returns the results of the lambda function as a list.</p>
|
|||
|
|
|||
|
<h4 id="series-1">Series</h4>
|
|||
|
<pre><code class="language-lisp">(iterate ((item (scan '(1 2 3))))
|
|||
|
(print item))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>scan-sublists</code> is the equivalent of <code>loop for ... on</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((i (scan-sublists '(1 2 3))))
|
|||
|
(print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="looping-over-a-vector">Looping over a vector</h3>
|
|||
|
|
|||
|
<h4 id="loop-across">loop: <code>across</code></h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i across #(1 2 3) do (print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-2">Series</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((i (scan #(1 2 3))))
|
|||
|
(print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="looping-over-a-hash-table">Looping over a hash-table</h3>
|
|||
|
|
|||
|
<p>We create a hash-table:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(defparameter h (make-hash-table))
|
|||
|
(setf (gethash 'a h) 1)
|
|||
|
(setf (gethash 'b h) 2)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="looping-over-keys-and-values">Looping over keys and values</h4>
|
|||
|
|
|||
|
<p>Looping over keys:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for k being the hash-key of h do (print k))
|
|||
|
;; b
|
|||
|
;; a
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Looping over values uses the same concept but with the <code>hash-value</code> keyword instead of <code>hash-key</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for k being the hash-value of h do (print k))
|
|||
|
;; 1
|
|||
|
;; 2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Looping over key-values pairs:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for k
|
|||
|
being the hash-key
|
|||
|
using (hash-value v) of h
|
|||
|
do (format t "~a ~a~%" k v))
|
|||
|
b 2
|
|||
|
a 1
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="iterate">iterate</h4>
|
|||
|
|
|||
|
<p>Use <code>in-hashtable</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for (key value) in-hashtable h)
|
|||
|
(collect (list key value)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="for">for</h4>
|
|||
|
|
|||
|
<p>the same with <code>for</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(for:for ((it over h))
|
|||
|
(print it))
|
|||
|
(A 1)
|
|||
|
(B 2)
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="maphash">maphash</h4>
|
|||
|
|
|||
|
<p>The lambda function of <code>maphash</code> takes two arguments: the key and the
|
|||
|
value:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(maphash (lambda (key val)
|
|||
|
(format t "key: ~a val:~a~&" key val))
|
|||
|
h)
|
|||
|
;; key: A val:1
|
|||
|
;; key: B val:2
|
|||
|
;; NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>See also <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_w_hash.htm">with-hash-table-iterator</a>.</p>
|
|||
|
|
|||
|
<h4 id="dohash">dohash</h4>
|
|||
|
|
|||
|
<p>Only because we like this topic, we introduce another library, <a href="https://github.com/yitzchak/trivial-do/">trivial-do</a>. It has the <code>dohash</code> macro, that ressembles <code>dolist</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(dohash (key value h)
|
|||
|
(format t "key: ~A, value: ~A~%" key value))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-3">Series</h4>
|
|||
|
<pre><code class="language-lisp">(iterate (((k v) (scan-hash h)))
|
|||
|
(format t "~&~a ~a~%" k v))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="looping-over-two-lists-in-parallel">Looping over two lists in parallel</h3>
|
|||
|
|
|||
|
<h4 id="loop-1">loop</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(a b c)
|
|||
|
for y in '(1 2 3)
|
|||
|
collect (list x y))
|
|||
|
;; ((A 1) (B 2) (C 3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>To return a flat list, use <code>nconcing</code> instead of <code>collect</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(a b c)
|
|||
|
for y in '(1 2 3)
|
|||
|
nconcing (list x y))
|
|||
|
(A 1 B 2 C 3)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>If a list is smaller than the other one, loop stops at the end of the small one:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(a b c)
|
|||
|
for y in '(1 2 3 4 5)
|
|||
|
collect (list x y))
|
|||
|
;; ((A 1) (B 2) (C 3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>We could loop over the biggest list and manually access the elements
|
|||
|
of the smaller one by index, but it would quickly be
|
|||
|
inefficient. Instead, we can tell <code>loop</code> to extend the short list.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for y in '(1 2 3 4 5)
|
|||
|
for x-list = '(a b c) then (cdr x-list)
|
|||
|
for x = (or (car x-list) 'z)
|
|||
|
collect (list x y))
|
|||
|
;; ((A 1) (B 2) (C 3) (Z 4) (Z 5))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The trick is that the notation <code>for … = … then (cdr …)</code> (note the <code>=</code>
|
|||
|
and the role of <code>then</code>) shortens our intermediate list at each
|
|||
|
iteration (thanks to <code>cdr</code>). It will first be <code>'(a b c)</code>, the initial
|
|||
|
value, then we will get the <code>cdr</code>: <code>(2 3)</code>, then <code>(3)</code>, then
|
|||
|
<code>NIL</code>. And both <code>(car NIL)</code> and <code>(cdr NIL)</code> return <code>NIL</code>, so we are
|
|||
|
good.</p>
|
|||
|
|
|||
|
<h4 id="mapcar-1">mapcar</h4>
|
|||
|
<pre><code class="language-lisp">(mapcar (lambda (x y)
|
|||
|
(list x y))
|
|||
|
'(a b c)
|
|||
|
'(1 2 3))
|
|||
|
;; ((A 1) (B 2) (C 3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>or simply:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(mapcar #'list
|
|||
|
'(a b c)
|
|||
|
'(1 2 3))
|
|||
|
;; ((A 1) (B 2) (C 3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Return a flat list:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(mapcan (lambda (x y)
|
|||
|
(list x y))
|
|||
|
'(a b c)
|
|||
|
'(1 2 3))
|
|||
|
;; (A 1 B 2 C 3)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-4">Series</h4>
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(#Mlist (scan '(a b c))
|
|||
|
(scan '(1 2 3))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>A more efficient way, when the lists are known to be of equal length:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(mapping (((x y) (scan-multiple 'list
|
|||
|
'(a b c)
|
|||
|
'(1 2 3))))
|
|||
|
(list x y)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Return a flat list:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect-append ; or collect-nconc
|
|||
|
(mapping (((x y) (scan-multiple 'list
|
|||
|
'(a b c)
|
|||
|
'(1 2 3))))
|
|||
|
(list x y)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="nested-loops">Nested loops</h3>
|
|||
|
<h4 id="loop-2">loop</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x from 1 to 3
|
|||
|
collect (loop for y from 1 to x
|
|||
|
collect y))
|
|||
|
;; ((1) (1 2) (1 2 3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>To return a flat list, use <code>nconcing</code> instead of the first <code>collect</code>.</p>
|
|||
|
|
|||
|
<h4 id="iterate-1">iterate</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter outer
|
|||
|
(for i below 2)
|
|||
|
(iter (for j below 3)
|
|||
|
(in outer (collect (list i j)))))
|
|||
|
;; ((0 0) (0 1) (0 2) (1 0) (1 1) (1 2))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-5">Series</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(mapping ((x (scan-range :from 1 :upto 3)))
|
|||
|
(collect (scan-range :from 1 :upto x))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="computing-an-intermediate-value">Computing an intermediate value</h3>
|
|||
|
|
|||
|
<p>Use <code>=</code>.</p>
|
|||
|
|
|||
|
<p>With <code>for</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x from 1 to 3
|
|||
|
for y = (* x 10)
|
|||
|
collect y)
|
|||
|
;; (10 20 30)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>With <code>with</code>, the difference being that the value is computed only
|
|||
|
once:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x from 1 to 3
|
|||
|
for y = (* x 10)
|
|||
|
with z = x
|
|||
|
collect (list x y z))
|
|||
|
;; ((1 10 1) (2 20 1) (3 30 1))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The HyperSpec defines the <code>with</code> clause like this:</p>
|
|||
|
|
|||
|
<pre><code>with-clause::= with var1 [type-spec] [= form1] {and var2 [type-spec] [= form2]}*
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>so it turns out we can specify the type before the <code>=</code> and chain the <code>with</code> with <code>and</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x from 1 to 3
|
|||
|
for y integer = (* x 10)
|
|||
|
with z integer = x
|
|||
|
collect (list x y z))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x upto 3
|
|||
|
with foo = :foo
|
|||
|
and bar = :bar
|
|||
|
collect (list x foo bar))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>We can also give <code>for</code> a <code>then</code> clause that will be called at each iteration:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop repeat 3
|
|||
|
for intermediate = 10 then (incf intermediate)
|
|||
|
do (print intermediate))
|
|||
|
10
|
|||
|
11
|
|||
|
12
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Here’s a trick to alternate a boolean:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop repeat 4
|
|||
|
for up = t then (not up)
|
|||
|
do (print up))
|
|||
|
|
|||
|
T
|
|||
|
NIL
|
|||
|
T
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="loop-with-a-counter">Loop with a counter</h3>
|
|||
|
<h4 id="loop-3">loop</h4>
|
|||
|
<p>Iterate through a list, and have a counter iterate in parallel. The length of
|
|||
|
the list determines when the iteration ends. Two sets of actions are defined,
|
|||
|
one of which is executed conditionally.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">* (loop for x in '(a b c d e)
|
|||
|
for y from 1
|
|||
|
|
|||
|
when (> y 1)
|
|||
|
do (format t ", ")
|
|||
|
|
|||
|
do (format t "~A" x)
|
|||
|
)
|
|||
|
|
|||
|
A, B, C, D, E
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>We could also write the preceding loop using the IF construct.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">* (loop for x in '(a b c d e)
|
|||
|
for y from 1
|
|||
|
|
|||
|
if (> y 1)
|
|||
|
do (format t ", ~A" x)
|
|||
|
else do (format t "~A" x)
|
|||
|
)
|
|||
|
|
|||
|
A, B, C, D, E
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-6">Series</h4>
|
|||
|
|
|||
|
<p>By iterating on multiple series in parallel, and using an infinite
|
|||
|
range, we can make a counter.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((x (scan '(a b c d e)))
|
|||
|
(y (scan-range :from 1)))
|
|||
|
(when (> y 1) (format t ", "))
|
|||
|
(format t "~A" x))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="ascending-descending-order-limits">Ascending, descending order, limits</h3>
|
|||
|
<h4 id="loop-4">loop</h4>
|
|||
|
|
|||
|
<p><code>from… to…</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 0 to 10
|
|||
|
do (print i))
|
|||
|
;; 0 1 2 3 4 5 6 7 8 9 10
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>from… below…</code>: this stops at 9:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 0 below 10
|
|||
|
do (print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Similarly, use <code>from 10 downto 0</code> (10…0) and <code>from 10 above 0</code> (10…1).</p>
|
|||
|
|
|||
|
<h4 id="series-7">Series</h4>
|
|||
|
|
|||
|
<p><code>:from ... :upto</code>, including the upper limit:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((i (scan-range :from 0 :upto 10)))
|
|||
|
(print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>:from ... :below</code>, excluding the upper limit:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((i (scan-range :from 0 :below 10)))
|
|||
|
(print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="steps">Steps</h3>
|
|||
|
<h4 id="loop-5">loop</h4>
|
|||
|
|
|||
|
<p>with <code>by</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 1 to 10 by 2
|
|||
|
do (print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>if you use <code>by (1+ (random 3))</code>, the random is evaluated only once, as
|
|||
|
if it was in a closure:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(let ((step (random 3)))
|
|||
|
(loop for i from 1 to 10 by (+ 1 step)
|
|||
|
do (print i)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The step must always be a positive number. If you want to count down, see above.</p>
|
|||
|
|
|||
|
<h4 id="series-8">Series</h4>
|
|||
|
|
|||
|
<p>with <code>:by</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iterate ((i (scan-range :from 1 :upto 10 :by 2)))
|
|||
|
(print i))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="loop-and-conditionals">Loop and conditionals</h3>
|
|||
|
<h4 id="loop-6">loop</h4>
|
|||
|
|
|||
|
<p>with <code>if</code>, <code>else</code> and <code>finally</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">;; https://riptutorial.com/common-lisp/example/11095/conditionally-executing-loop-clauses
|
|||
|
(loop repeat 10
|
|||
|
for x = (random 100)
|
|||
|
if (evenp x)
|
|||
|
collect x into evens
|
|||
|
else
|
|||
|
collect x into odds
|
|||
|
finally (return (values evens odds)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<pre><code>(42 82 24 92 92)
|
|||
|
(55 89 59 13 49)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Combining multiple clauses in an if body requires special syntax (<code>and
|
|||
|
do</code>, <code>and count</code>):</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp"> (loop repeat 10
|
|||
|
for x = (random 100)
|
|||
|
if (evenp x)
|
|||
|
collect x into evens
|
|||
|
and do (format t "~a is even!~%" x)
|
|||
|
else
|
|||
|
collect x into odds
|
|||
|
and count t into n-odds
|
|||
|
finally (return (values evens odds n-odds)))
|
|||
|
</code></pre>
|
|||
|
<pre><code>46 is even!
|
|||
|
8 is even!
|
|||
|
76 is even!
|
|||
|
58 is even!
|
|||
|
0 is even!
|
|||
|
(46 8 76 58 0)
|
|||
|
(7 45 43 15 69)
|
|||
|
5
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="iterate-2">iterate</h4>
|
|||
|
|
|||
|
<p>Translating (or even writing!) the above example using iterate is straight-forward:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (repeat 10)
|
|||
|
(for x = (random 100))
|
|||
|
(if (evenp x)
|
|||
|
(progn
|
|||
|
(collect x into evens)
|
|||
|
(format t "~a is even!~%" x))
|
|||
|
(progn
|
|||
|
(collect x into odds)
|
|||
|
(count t into n-odds)))
|
|||
|
(finally (return (values evens odds n-odds))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-9">Series</h4>
|
|||
|
|
|||
|
<p>The preceding loop would be done a bit differently in Series. <code>split</code>
|
|||
|
sorts one series into multiple according to provided boolean series.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(let* ((number (#M(lambda (n) (random 100))
|
|||
|
(scan-range :below 10)))
|
|||
|
(parity (#Mevenp number)))
|
|||
|
(iterate ((n number) (p parity))
|
|||
|
(when p (format t "~a is even!~%" n)))
|
|||
|
(multiple-value-bind (evens odds) (split number parity)
|
|||
|
(values (collect evens)
|
|||
|
(collect odds)
|
|||
|
(collect-length odds))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Note that although <code>iterate</code> and the three <code>collect</code> expressions are
|
|||
|
written sequentially, only one iteration is performed, the same as the
|
|||
|
example with loop.</p>
|
|||
|
|
|||
|
<h3 id="begin-the-loop-with-a-clause-initially">Begin the loop with a clause (initially)</h3>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop initially
|
|||
|
(format t "~a " 'loop-begin)
|
|||
|
for x below 3
|
|||
|
do (format t "~a " x))
|
|||
|
;; LOOP-BEGIN 0 1 2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><code>initially</code> also exists with <code>iterate</code>.</p>
|
|||
|
|
|||
|
<h3 id="terminate-the-loop-with-a-test-until-while">Terminate the loop with a test (until, while)</h3>
|
|||
|
<h4 id="loop-7">loop</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(1 2 3 4 5)
|
|||
|
until (> x 3)
|
|||
|
collect x)
|
|||
|
;; (1 2 3)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>the same, with <code>while</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(1 2 3 4 5)
|
|||
|
while (< x 4)
|
|||
|
collect x)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-10">Series</h4>
|
|||
|
|
|||
|
<p>We truncate the series with <code>until-if</code>, then collect from its result.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(until-if (lambda (i) (> i 3))
|
|||
|
(scan '(1 2 3 4 5))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="loop-print-and-return-a-result">Loop, print and return a result</h3>
|
|||
|
<h4 id="loop-8">loop</h4>
|
|||
|
|
|||
|
<p><code>do</code> and <code>collect</code> can be combined in one expression</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x in '(1 2 3 4 5)
|
|||
|
while (< x 4)
|
|||
|
do (format t "x is ~a~&" x)
|
|||
|
collect x)
|
|||
|
x is 1
|
|||
|
x is 2
|
|||
|
x is 3
|
|||
|
(1 2 3)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-11">Series</h4>
|
|||
|
<p>By mapping, we can perform a side effect and also collect items</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(mapping ((x (until-if (complement (lambda (x) (< x 4)))
|
|||
|
(scan '(1 2 3 4 5)))))
|
|||
|
(format t "x is ~a~&" x)
|
|||
|
x))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="named-loops-and-early-exit">Named loops and early exit</h3>
|
|||
|
<h4 id="loop-9">loop</h4>
|
|||
|
|
|||
|
<p>The special <code>loop named</code> foo syntax allows you to create a loop that
|
|||
|
you can exit early from. The exit is performed using <code>return-from</code>,
|
|||
|
and can be used from within nested loops.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">;; useless example
|
|||
|
(loop named loop-1
|
|||
|
for x from 0 to 10 by 2
|
|||
|
do (loop for y from 0 to 100 by (1+ (random 3))
|
|||
|
when (< x y)
|
|||
|
do (return-from loop-1 (values x y))))
|
|||
|
0
|
|||
|
2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Sometimes, you want to return early but execute the <code>finally</code> clause
|
|||
|
anyways. Use <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_loop_f.htm#loop-finish"><code>loop-finish</code></a>.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for x from 0 to 100
|
|||
|
do (print x)
|
|||
|
when (>= x 3)
|
|||
|
return x
|
|||
|
finally (print :done)) ;; <-- not printed
|
|||
|
;; 0
|
|||
|
;; 1
|
|||
|
;; 2
|
|||
|
;; 3
|
|||
|
;; 3
|
|||
|
|
|||
|
(loop for x from 0 to 100
|
|||
|
do (print x)
|
|||
|
when (>= x 3)
|
|||
|
do (loop-finish)
|
|||
|
finally (print :done)
|
|||
|
(return x))
|
|||
|
;; 0
|
|||
|
;; 1
|
|||
|
;; 2
|
|||
|
;; 3
|
|||
|
;; :DONE
|
|||
|
;; 3
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>It is most needed when some computation must take place in the <code>finally</code> clause.</p>
|
|||
|
|
|||
|
<h4 id="loop-shorthands-for-whenreturn">Loop shorthands for when/return</h4>
|
|||
|
|
|||
|
<p>Several actions provide shorthands for combinations of when/return:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">* (loop for x in '(foo 2)
|
|||
|
thereis (numberp x))
|
|||
|
T
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<pre><code class="language-lisp">* (loop for x in '(foo 2)
|
|||
|
never (numberp x))
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<pre><code class="language-lisp">* (loop for x in '(foo 2)
|
|||
|
always (numberp x))
|
|||
|
NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>They correspond to the functions <code>some</code>, <code>notany</code> and <code>every</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(some #'numberp '(foo 2))
|
|||
|
(notany #'numberp '(foo 2))
|
|||
|
(every #'numberp '(foo 2))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-12">Series</h4>
|
|||
|
|
|||
|
<p>A block is manually created and returned from.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(block loop-1
|
|||
|
(iterate ((x (scan-range :from 0 :upto 10 :by 2)))
|
|||
|
(iterate ((y (scan-range :from 0 :upto 100 :by (1+ (random 3)))))
|
|||
|
(when (< x y)
|
|||
|
(return-from loop-1 (values x y))))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="count">Count</h3>
|
|||
|
<h4 id="loop-10">loop</h4>
|
|||
|
<pre><code class="language-lisp">(loop for i from 1 to 3 count (oddp i))
|
|||
|
;; 2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-13">Series</h4>
|
|||
|
<pre><code class="language-lisp">(collect-length (choose-if #'oddp (scan-range :from 1 :upto 3)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="summation">Summation</h3>
|
|||
|
<h4 id="loop-11">loop</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 1 to 3 sum (* i i))
|
|||
|
;; 14
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Summing into a variable:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 1 to 3
|
|||
|
sum (* i i) into total
|
|||
|
do (print i)
|
|||
|
finally (print total))
|
|||
|
1
|
|||
|
2
|
|||
|
3
|
|||
|
14
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-14">Series</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect-sum (#M(lambda (i) (* i i))
|
|||
|
(scan-range :from 1 :upto 3)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="max-min">max, min</h3>
|
|||
|
|
|||
|
<h4 id="loop-12">loop</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 1 to 3 maximize (mod i 3))
|
|||
|
;; 2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>and <code>minimize</code>.</p>
|
|||
|
|
|||
|
<h4 id="series-15">Series</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect-max (#M(lambda (i) (mod i 3))
|
|||
|
(scan-range :from 1 :upto 3)))
|
|||
|
</code></pre>
|
|||
|
<p>and <code>collect-min</code>.</p>
|
|||
|
|
|||
|
<h3 id="destructuring-aka-pattern-matching-against-the-list-or-dotted-pairs">Destructuring, aka pattern matching against the list or dotted pairs</h3>
|
|||
|
|
|||
|
<h4 id="loop-13">loop</h4>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for (a b) in '((x 1) (y 2) (z 3))
|
|||
|
collect (list b a) )
|
|||
|
;; ((1 X) (2 Y) (3 Z))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for (x . y) in '((1 . a) (2 . b) (3 . c)) collect y)
|
|||
|
;; (A B C)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Use <code>nil</code> to ignore a term:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for (a nil) in '((x 1) (y 2) (z 3))
|
|||
|
collect a )
|
|||
|
;; (X Y Z)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h5 id="iterating-2-by-2-over-a-list">Iterating 2 by 2 over a list</h5>
|
|||
|
|
|||
|
<p>To iterate over a list, 2 items at a time we use a combination of <code>on</code>, <code>by</code> and destructuring.</p>
|
|||
|
|
|||
|
<p>We use <code>on</code> to loop over the rest (the <code>cdr</code>) of the list.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for rest on '(a 2 b 2 c 3)
|
|||
|
collect rest)
|
|||
|
;; ((A 2 B 2 C 3) (2 B 2 C 3) (B 2 C 3) (2 C 3) (C 3) (3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>We use <code>by</code> to skip one element at every iteration (<code>(cddr list)</code> is equivalent to <code>(rest (rest list))</code>)</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for rest on '(a 2 b 2 c 3) by #'cddr
|
|||
|
collect rest)
|
|||
|
;; ((A 2 B 2 C 3) (B 2 C 3) (C 3))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Then we add destructuring to bind only the first two items at each iteration:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for (key value) on '(a 2 b 2 c 3) by #'cddr
|
|||
|
collect (list key (* 2 value)))
|
|||
|
;; ((A 2) (B 4) (C 6))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h4 id="series-16">Series</h4>
|
|||
|
<p>In general, with <code>destructuring-bind</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(mapping ((l (scan '((x 1) (y 2) (z 3)))))
|
|||
|
(destructuring-bind (a b) l
|
|||
|
(list b a))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>But for alists, <code>scan-alist</code> is provided:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect
|
|||
|
(mapping (((a b) (scan-alist '((1 . a) (2 . b) (3 . c)))))
|
|||
|
b))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h2 id="iterate-unique-features-lacking-in-loop">Iterate unique features lacking in loop</h2>
|
|||
|
|
|||
|
<p><code>iterate</code> has some other things unique to it.</p>
|
|||
|
|
|||
|
<p>If you are a newcomer in Lisp, it’s perfectly OK to keep this section for
|
|||
|
later. You could very well spend your career in Lisp without resorting
|
|||
|
to those features… although they might turn out useful one day.</p>
|
|||
|
|
|||
|
<h3 id="no-rigid-order-for-clauses">No rigid order for clauses</h3>
|
|||
|
|
|||
|
<p><code>loop</code> requires that all <code>for</code> clauses appear before the loop body,
|
|||
|
for example before a <code>while</code>. It’s ok for <code>iter</code> to not follow this
|
|||
|
order:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for x in '(1 2 99)
|
|||
|
(while (< x 10))
|
|||
|
(for y = (print x))
|
|||
|
(collect (list x y)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="accumulating-clauses-can-be-nested">Accumulating clauses can be nested</h3>
|
|||
|
|
|||
|
<p><code>collect</code>, <code>appending</code> and other accumulating clauses can appear anywhere:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for x in '(1 2 3))
|
|||
|
(case x
|
|||
|
(1 (collect :a))
|
|||
|
;; ^^ iter keyword, nested in a s-expression.
|
|||
|
(2 (collect :b))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="finders-finding">Finders: <code>finding</code></h3>
|
|||
|
|
|||
|
<p><code>iterate</code> has <a href="https://common-lisp.net/project/iterate/doc/Finders.html#Finders">finders</a>.</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>A finder is a clause whose value is an expression that meets some condition.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>We can use <code>finding</code> followed by <code>maximizing</code>, <code>minimizing</code> or <code>such-that</code>.</p>
|
|||
|
|
|||
|
<p>Here’s how to find the longest list in a list of lists:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for elt in '((a) (b c d) (e f)))
|
|||
|
(finding elt maximizing (length elt)))
|
|||
|
=> (B C D)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>The rough equivalent in LOOP would be:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop with max-elt = nil
|
|||
|
with max-key = 0
|
|||
|
for elt in '((a) (b c d) (e f))
|
|||
|
for key = (length elt)
|
|||
|
do
|
|||
|
(when (> key max-key)
|
|||
|
(setf max-elt elt
|
|||
|
max-key key))
|
|||
|
finally (return max-elt))
|
|||
|
=> (B C D)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>There could be more than one <code>such-that</code> clause:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp"> (iter (for i in '(7 -4 2 -3))
|
|||
|
(if (plusp i)
|
|||
|
(finding i such-that (evenp i))
|
|||
|
(finding (- i) such-that (oddp i))))
|
|||
|
;; => 2
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>We can also write <code>such-that #'evenp</code> and <code>such-that #'oddp</code>.</p>
|
|||
|
|
|||
|
<h3 id="control-flow-next-iteration">Control flow: <code>next-iteration</code></h3>
|
|||
|
|
|||
|
<p>It is like “continue” and loop doesn’t have it.</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Skips the remainder of the loop body and begins the next iteration of the loop.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p><code>iterate</code> also has <code>first-iteration-p</code> and <code>(if-first-time then else)</code>.</p>
|
|||
|
|
|||
|
<p>See <a href="https://common-lisp.net/project/iterate/doc/Control-Flow.html#Control-Flow">control flow</a>.</p>
|
|||
|
|
|||
|
<h3 id="generators">Generators</h3>
|
|||
|
|
|||
|
<p>Use <code>generate</code> and <code>next</code>. A generator is lazy, it goes to the next value when said explicitly.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for i in '(1 2 3 4 5))
|
|||
|
(generate c in-string "black")
|
|||
|
(if (oddp i) (next c))
|
|||
|
(format t "~a " c))
|
|||
|
;; b b l l a
|
|||
|
;; NIL
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="variable-backtracking-previous-vs-parallel-binding">Variable backtracking (<code>previous</code>) VS parallel binding</h3>
|
|||
|
|
|||
|
<p><code>iterate</code> allows us to get the previous value of a variable:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for el in '(a b c d e))
|
|||
|
(for prev-el previous el)
|
|||
|
(collect (list el prev-el)))
|
|||
|
;; => ((A NIL) (B A) (C B) (D C) (E D))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>In this case however we can do it with <code>loop</code>’s parallel binding <code>and</code>, which is unsupported in <code>iterate</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for el in '(a b c d e)
|
|||
|
and prev-el = nil then el
|
|||
|
collect (list el prev-el))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="more-clauses">More clauses</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><code>in-string</code> can be used explicitly to iterate character by character over a string. With loop, use <code>across</code>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for c in-string "hello")
|
|||
|
(collect c))
|
|||
|
;; => (#\h #\e #\l #\l #\o)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><code>loop</code> offers <code>collecting</code>, <code>nconcing</code>, and <code>appending</code>. <code>iterate</code> has these and also <code>adjoining</code>, <code>unioning</code>, <code>nunioning</code>, and <code>accumulating</code>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for el in '(a b c a d b))
|
|||
|
(adjoining el))
|
|||
|
;; => (A B C D)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>(<code>adjoin</code> is a set operation)</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><code>loop</code> has <code>summing</code>, <code>counting</code>, <code>maximizing</code>, and <code>minimizing</code>. <code>iterate</code> also includes <code>multiplying</code> and <code>reducing</code>. reducing is the generalized reduction builder:</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (with dividend = 100)
|
|||
|
(for divisor in '(10 5 2))
|
|||
|
(reducing divisor by #'/ initial-value dividend))
|
|||
|
;; => 1
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h3 id="iterate-is-extensible">Iterate is extensible</h3>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(defmacro dividing-by (num &keys (initial-value 0))
|
|||
|
`(reducing ,num by #'/ initial-value ,initial-value))
|
|||
|
|
|||
|
(iter (for i in '(10 5 2))
|
|||
|
(dividing-by i :initial-value 100))
|
|||
|
=> 1
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>but <a href="https://common-lisp.net/project/iterate/doc/Rolling-Your-Own.html#Rolling-Your-Own">there is more to it, see the documentation</a>.</p>
|
|||
|
|
|||
|
<p>We saw libraries extending <code>loop</code>, for example <a href="http://clsql.kpe.io/manual/loop-tuples.html">CLSQL</a>, but they are
|
|||
|
full of feature flag checks (<code>#+(or allegro clisp-aloop cmu openmcl
|
|||
|
sbcl scl)</code>) and they call internal modules
|
|||
|
(<code>ansi-loop::add-loop-path</code>, <code>sb-loop::add-loop-path</code> etc).</p>
|
|||
|
|
|||
|
<h2 id="custom-series-scanners">Custom series scanners</h2>
|
|||
|
|
|||
|
<p>If we often scan the same type of object, we can write our own scanner
|
|||
|
for it: the iteration itself can be factored out. Taking the example
|
|||
|
above, of scanning a list of two-element lists, we’ll write a scanner
|
|||
|
that returns a series of the first elements and a series of the
|
|||
|
second.</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(defun scan-listlist (listlist)
|
|||
|
(declare (optimizable-series-function 2))
|
|||
|
(map-fn '(values t t)
|
|||
|
(lambda (l)
|
|||
|
(destructuring-bind (a b) l
|
|||
|
(values a b)))
|
|||
|
(scan listlist)))
|
|||
|
|
|||
|
(collect
|
|||
|
(mapping (((a b) (scan-listlist '((x 1) (y 2) (z 3)))))
|
|||
|
(list b a)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h2 id="shorter-series-expressions">Shorter series expressions</h2>
|
|||
|
|
|||
|
<p>Consider this series expression:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect-sum (mapping ((i (scan-range :length 5)))
|
|||
|
(* i 2)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>It’s a bit longer than it needs to be, the <code>mapping</code> form’s only
|
|||
|
purpose is to bind the variable <code>i</code>, and <code>i</code> is used in only one
|
|||
|
place. Series has a “hidden feature” that allows us to simplify this
|
|||
|
expression to the following:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(collect-sum (* 2 (scan-range :length 5)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>This is called implicit mapping and can be enabled in the call to
|
|||
|
<code>series::install</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(series::install :implicit-map t)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>When using implicit mapping, the <code>#M</code> reader macro demonstrated above
|
|||
|
becomes redundant.</p>
|
|||
|
|
|||
|
<h2 id="loop-gotchas">Loop gotchas</h2>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>the keyword <code>it</code>, often used in functional constructs, can be
|
|||
|
recognized as a loop keyword. Don’t use it inside a loop.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h2 id="iterate-gotchas">Iterate gotchas</h2>
|
|||
|
|
|||
|
<p>It breaks on the function <code>count</code>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(iter (for i from 1 to 10)
|
|||
|
(sum (count i '(1 3 5))))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>It doesn’t recognize the built-in <code>count</code> function and instead signals a condition.</p>
|
|||
|
|
|||
|
<p>It works in loop:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for i from 1 to 10
|
|||
|
sum (count i '(1 3 5 99)))
|
|||
|
;; 3
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<h2 id="appendix-list-of-loop-keywords">Appendix: list of loop keywords</h2>
|
|||
|
|
|||
|
<p><strong>Name Clause</strong></p>
|
|||
|
|
|||
|
<pre><code>named
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><strong>Variable Clauses</strong></p>
|
|||
|
|
|||
|
<pre><code>initially finally for as with
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><strong>Main Clauses</strong></p>
|
|||
|
|
|||
|
<pre><code>do collect collecting append
|
|||
|
appending nconc nconcing into count
|
|||
|
counting sum summing maximize return loop-finish
|
|||
|
maximizing minimize minimizing doing
|
|||
|
thereis always never if when
|
|||
|
unless repeat while until
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>These don’t introduce clauses:</p>
|
|||
|
|
|||
|
<pre><code>= and it else end from upfrom
|
|||
|
above below to upto downto downfrom
|
|||
|
in on then across being each the hash-key
|
|||
|
hash-keys of using hash-value hash-values
|
|||
|
symbol symbols present-symbol
|
|||
|
present-symbols external-symbol
|
|||
|
external-symbols fixnum float t nil of-type
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>But note that it’s the parsing that determines what is a keyword. For example in:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(loop for key in hash-values)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Only <code>for</code> and <code>in</code> are keywords.</p>
|
|||
|
|
|||
|
<p>©Dan Robertson on <a href="https://stackoverflow.com/questions/52236803/list-of-loop-keywords">Stack Overflow</a>.</p>
|
|||
|
|
|||
|
<h2 id="credit-and-references">Credit and references</h2>
|
|||
|
|
|||
|
<h3 id="loop-14">Loop</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="http://www.ai.sri.com/pkarp/loop.html">Tutorial for the Common Lisp Loop Macro</a> by Peter D. Karp</li>
|
|||
|
<li><a href="http://www.unixuser.org/~euske/doc/cl/loop.html">Common Lisp’s Loop Macro Examples for Beginners</a> by Yusuke Shinyama</li>
|
|||
|
<li><a href="https://gitlab.com/vancan1ty/clstandard_build">Section 6.1 The LOOP Facility, of the draft Common Lisp Standard (X3J13/94-101R)</a> - the (draft) standard provides background information on Loop development, specification and examples. <a href="https://gitlab.com/vancan1ty/clstandard_build/-/blob/master/cl-ansi-standard-draft-w-sidebar.pdf">Single PDF file available</a></li>
|
|||
|
<li><a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node235.html">26. Loop by Jon L White, edited and expanded by Guy L. Steele Jr.</a> - from the book “Common Lisp the Language, 2nd Edition”. Strong connection to the draft above, with supplementing comments and examples.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="iterate-3">Iterate</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://common-lisp.net/project/iterate/doc/index.html">The Iterate Manual</a> -by Jonathan Amsterdam and Luís Oliveira</li>
|
|||
|
<li><a href="https://common-lisp-libraries.readthedocs.io/iterate/">iterate - Pseudocodic Iteration</a> - by Shubhamkar Ayare</li>
|
|||
|
<li><a href="https://sites.google.com/site/sabraonthehill/loop-v-iter">Loop v Iterate - SabraOnTheHill</a></li>
|
|||
|
<li><a href="https://web.archive.org/web/20170713081006/https://items.sjbach.com/211/comparing-loop-and-iterate">Comparing loop and iterate</a> - by Stephen Bach (web archive)</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="series-17">Series</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html">Common Lisp the Language (2nd Edition) - Appendix A. Series</a></li>
|
|||
|
<li><a href="http://series.sourceforge.net/">SERIES for Common Lisp - Richard C. Waters</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="others">Others</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>See also: <a href="https://lisp-journey.gitlab.io/blog/snippets-functional-style-more/">more functional constructs</a> (do-repeat, take,…)</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
|
|||
|
<p class="page-source">
|
|||
|
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/iteration.md">iteration.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/>
|
|||
|
© 2002–2023 the Common Lisp Cookbook Project
|
|||
|
<div>
|
|||
|
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">vindarel's Lisp course on Udemy</a>
|
|||
|
</div>
|
|||
|
</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>
|