325 lines
10 KiB
HTML
325 lines
10 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta name="generator" content=
|
||
"HTML Tidy for HTML5 for Linux version 5.2.0">
|
||
<title>Pattern Matching</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> – Pattern Matching</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> – Pattern Matching</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=LISPY-XMAS2023" 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 our contributor's Lisp course with this Christmas 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>The ANSI Common Lisp standard does not include facilities for pattern
|
||
matching, but libraries existed for this task and
|
||
<a href="https://github.com/guicho271828/trivia">Trivia</a> became a community
|
||
standard.</p>
|
||
|
||
<p>For an introduction to the concepts of pattern matching, see <a href="https://github.com/guicho271828/trivia/wiki/What-is-pattern-matching%3F-Benefits%3F">Trivia’s wiki</a>.</p>
|
||
|
||
<p>Trivia matches against <em>a lot</em> of lisp objects and is extensible.</p>
|
||
|
||
<p>The library is in Quicklisp:</p>
|
||
|
||
<pre><code class="language-lisp">(ql:quickload "trivia")
|
||
</code></pre>
|
||
|
||
<p>For the following examples, let’s <code>use</code> the library:</p>
|
||
|
||
<pre><code class="language-lisp">(use-package :trivia)
|
||
</code></pre>
|
||
|
||
<h2 id="common-destructuring-patterns">Common destructuring patterns</h2>
|
||
|
||
<h3 id="cons"><code>cons</code></h3>
|
||
|
||
<pre><code class="language-lisp">(match '(1 2 3)
|
||
((cons x y)
|
||
; ^^ pattern
|
||
(print x)
|
||
(print y)))
|
||
;; |-> 1
|
||
;; |-> (2 3)
|
||
</code></pre>
|
||
|
||
<h3 id="list-list"><code>list</code>, <code>list*</code></h3>
|
||
|
||
<p><code>list</code> is a strict pattern, it expects the length of the matched
|
||
object to be the same length as its subpatterns.</p>
|
||
|
||
<pre><code class="language-lisp">(match '(something 2 3)
|
||
((list a b _)
|
||
(values a b)))
|
||
SOMETHING
|
||
2
|
||
</code></pre>
|
||
|
||
<p>Without the <code>_</code> placeholder, it would not match:</p>
|
||
|
||
<pre><code class="language-lisp">(match '(something 2 3)
|
||
((list a b)
|
||
(values a b)))
|
||
NIL
|
||
</code></pre>
|
||
|
||
<p>The <code>list*</code> pattern is flexible on the object’s length:</p>
|
||
|
||
<pre><code class="language-lisp">(match '(something 2 3)
|
||
((list* a b)
|
||
(values a b)))
|
||
SOMETHING
|
||
(2 3)
|
||
</code></pre>
|
||
|
||
<pre><code class="language-lisp">(match '(1 2 . 3)
|
||
((list* _ _ x)
|
||
x))
|
||
3
|
||
</code></pre>
|
||
|
||
<p>However pay attention that if <code>list*</code> receives only one object, that
|
||
object is returned, regardless of whether or not it is a list:</p>
|
||
|
||
<pre><code class="language-lisp">(match #(0 1 2)
|
||
((list* a)
|
||
a))
|
||
#(0 1 2)
|
||
</code></pre>
|
||
|
||
<p>This is related to the definition of <code>list*</code> in the HyperSpec: http://clhs.lisp.se/Body/f_list_.htm.</p>
|
||
|
||
<h3 id="vector-vector"><code>vector</code>, <code>vector*</code></h3>
|
||
|
||
<p><code>vector</code> checks if the object is a vector, if the lengths are the
|
||
same, and if the contents matches against each subpatterns.</p>
|
||
|
||
<p><code>vector*</code> is similar, but called a soft-match variant that allows if
|
||
the length is larger-than-equal to the length of subpatterns.</p>
|
||
|
||
<pre><code class="language-lisp">(match #(1 2 3)
|
||
((vector _ x _)
|
||
x))
|
||
;; -> 2
|
||
</code></pre>
|
||
|
||
<pre><code class="language-lisp">(match #(1 2 3 4)
|
||
((vector _ x _)
|
||
x))
|
||
;; -> NIL : does not match
|
||
</code></pre>
|
||
|
||
<pre><code class="language-lisp">(match #(1 2 3 4)
|
||
((vector* _ x _)
|
||
x))
|
||
;; -> 2 : soft match.
|
||
</code></pre>
|
||
|
||
<pre><code><vector-pattern> : vector | simple-vector
|
||
bit-vector | simple-bit-vector
|
||
string | simple-string
|
||
base-string | simple-base-string | sequence
|
||
(<vector-pattern> &rest subpatterns)
|
||
</code></pre>
|
||
|
||
<h3 id="class-and-structure-pattern">Class and structure pattern</h3>
|
||
|
||
<p>There are three styles that are equivalent:</p>
|
||
|
||
<pre><code class="language-lisp">(defstruct foo bar baz)
|
||
(defvar *x* (make-foo :bar 0 :baz 1)
|
||
|
||
(match *x*
|
||
;; make-instance style
|
||
((foo :bar a :baz b)
|
||
(values a b))
|
||
;; with-slots style
|
||
((foo (bar a) (baz b))
|
||
(values a b))
|
||
;; slot name style
|
||
((foo bar baz)
|
||
(values bar baz)))
|
||
</code></pre>
|
||
|
||
<h3 id="type-satisfies"><code>type</code>, <code>satisfies</code></h3>
|
||
|
||
<p>The <code>type</code> pattern matches if the object is of type. <code>satisfies</code> matches
|
||
if the predicate returns true for the object. A lambda form is
|
||
acceptable.</p>
|
||
|
||
<h3 id="assoc-property-alist-plist"><code>assoc</code>, <code>property</code>, <code>alist</code>, <code>plist</code></h3>
|
||
|
||
<p>All these patterns first check if the pattern is a list. If that is
|
||
satisfied, then they obtain the contents, and the value is matched
|
||
against the subpattern.</p>
|
||
|
||
<h3 id="array-simple-array-row-major-array-patterns">Array, simple-array, row-major-array patterns</h3>
|
||
|
||
<p>See https://github.com/guicho271828/trivia/wiki/Type-Based-Destructuring-Patterns#array-simple-array-row-major-array-pattern !</p>
|
||
|
||
<h2 id="logic-based-patterns">Logic based patterns</h2>
|
||
|
||
<p>We can combine any pattern with some logic.</p>
|
||
|
||
<h3 id="and-or"><code>and</code>, <code>or</code></h3>
|
||
|
||
<p>The following:</p>
|
||
|
||
<pre><code class="language-lisp">(match x
|
||
((or (list 1 a)
|
||
(cons a 3))
|
||
a))
|
||
</code></pre>
|
||
|
||
<p>matches against both <code>(1 2)</code> and <code>(4 . 3)</code> and returns 2 and 4, respectively.</p>
|
||
|
||
<h3 id="not"><code>not</code></h3>
|
||
|
||
<p>It does not match when subpattern matches. The variables used in the
|
||
subpattern are not visible in the body.</p>
|
||
|
||
<h2 id="guards">Guards</h2>
|
||
|
||
<p>Guards allow us to use patterns <em>and</em> to verify them against a predicate.</p>
|
||
|
||
<p>The syntax is <code>guard</code> + <code>subpattern</code> + <code>a test form</code>, and the body.</p>
|
||
|
||
<pre><code class="language-lisp">(match (list 2 5)
|
||
((guard (list x y) ; subpattern1
|
||
(= 10 (* x y))) ; test-form
|
||
:ok))
|
||
</code></pre>
|
||
|
||
<p>If the subpattern is true, the test form is evaluated, and if it is
|
||
true it is matched against subpattern1.</p>
|
||
|
||
<h2 id="nesting-patterns">Nesting patterns</h2>
|
||
|
||
<p>Patterns can be nested:</p>
|
||
|
||
<pre><code class="language-lisp">(match '(:a (3 4) 5)
|
||
((list :a (list _ c) _)
|
||
c))
|
||
</code></pre>
|
||
|
||
<p>returns <code>4</code>.</p>
|
||
|
||
<h2 id="see-more">See more</h2>
|
||
|
||
<p>See <a href="https://github.com/guicho271828/trivia/wiki/Special-Patterns">special patterns</a>: <code>place</code>, <code>bind</code> and <code>access</code>.</p>
|
||
|
||
|
||
<p class="page-source">
|
||
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/pattern_matching.md">pattern_matching.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>
|