221 lines
8.7 KiB
HTML
221 lines
8.7 KiB
HTML
![]() |
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta name="generator" content=
|
|||
|
"HTML Tidy for HTML5 for Linux version 5.2.0">
|
|||
|
<title>Miscellaneous</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> – Miscellaneous</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> – Miscellaneous</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"
|
|||
|
<p><a name="opt"></a></p>
|
|||
|
|
|||
|
<h2 id="re-using-complex-data-structures">Re-using complex data structures</h2>
|
|||
|
|
|||
|
<p>Sometimes you want your functions to behave in a ‘functional’ way, i.e. return <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#fresh">fresh</a> results without side effects, sometimes you want them to re-use and modify existing data in a destructive way - consider the difference between <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_append.htm"><code>append</code></a> and <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_nconc.htm"><code>nconc</code></a> for an example.</p>
|
|||
|
|
|||
|
<p>Well, you can have your cake and eat it too, by using optional (or keyword) parameters. Here’s an example: Let’s assume you’re writing a function <code>complex-matrix-stuff</code> which takes two matrices <code>m1</code> and <code>m2</code> as its arguments and computes and returns a resulting matrix the size of which depends on <code>m1</code> and <code>m2</code>, i.e. for a fresh result you’ll need an empty matrix which’ll be created by, say, <code>(make-appropriate-result-matrix-for m1 m2)</code>.</p>
|
|||
|
|
|||
|
<p>The classical textbook way to implement this function will more or less look like this:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(defun complex-matrix-stuff (m1 m2)
|
|||
|
(let ((result (make-appropriate-result-matrix-for m1 m2)))
|
|||
|
;; ... compute storing the results in RESULT
|
|||
|
result))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>And you’ll use it like this:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(setq some-matrix (complex-matrix-stuff A B))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>But why not write it like so:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(defun complex-matrix-stuff (m1 m2
|
|||
|
&optional
|
|||
|
(result
|
|||
|
(make-appropriate-result-matrix-for m1 m2)))
|
|||
|
;; ... compute storing the results in RESULT
|
|||
|
result)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Now you have it both ways. You can still “make up results” on the fly as in:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(setq some-matrix (complex-matrix-stuff A B))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>But you can also (destructively) re-use previously allocated matrices:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(complex-matrix-stuff A B some-appropriate-matrix-I-built-before)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Or use your function like this:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(setq some-other-matrix
|
|||
|
(complex-matrix-stuff A B some-appropriate-matrix-I-built-before))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>in which case you’ll end up with:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">* (eq some-other-matrix some-appropriate-matrix-I-built-before)
|
|||
|
|
|||
|
T
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p><a name="adjust"></a></p>
|
|||
|
|
|||
|
<h2 id="using-adjust-array-instead-of-consing-up-new-sequences-with-subseq">Using <code>ADJUST-ARRAY</code> instead of consing up new sequences with <code>SUBSEQ</code></h2>
|
|||
|
|
|||
|
<p>Most CL functions operating on sequences will accept <code>start</code> and <code>end</code> keywords so you can make them operate on a sub-sequence without actually creating it, i.e. instead of</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(count #\a (subseq long-string from to))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>you should of course use</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(count #\a long-string :start from :end to)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>which’ll yield the same result but not create an unnecessary intermediate sub-sequence.</p>
|
|||
|
|
|||
|
<p>However, sometimes it looks like you can’t avoid creating new data. Consider a hash table the keys of which are strings. If the key you’re looking for is a sub-string of another string you’ll most likely end up with</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(gethash (subseq original-string from to)
|
|||
|
hash-table)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>But you don’t have to. You can create <em>one</em> <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_d.htm#displaced_array">displaced</a> string and reuse it multiple times with <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_adjust.htm"><code>adjust-array</code></a>:</p>
|
|||
|
|
|||
|
<pre><code class="language-lisp">(let ((substring (make-array 0
|
|||
|
:element-type 'character
|
|||
|
:displaced-to ""
|
|||
|
:displaced-index-offset 0)))
|
|||
|
;; more code
|
|||
|
(gethash
|
|||
|
(adjust-array substring (- to from)
|
|||
|
:displaced-to original-string
|
|||
|
:displaced-index-offset from)
|
|||
|
hash-table)
|
|||
|
;; even more code
|
|||
|
)
|
|||
|
</code></pre>
|
|||
|
|
|||
|
|
|||
|
<p class="page-source">
|
|||
|
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/misc.md">misc.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>
|