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

317 lines
9.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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>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> &ndash; 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> &ndash; 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 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>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">Trivias 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, lets <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)))
;; |-&gt; 1
;; |-&gt; (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 objects 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))
;; -&gt; 2
</code></pre>
<pre><code class="language-lisp">(match #(1 2 3 4)
((vector _ x _)
x))
;; -&gt; NIL : does not match
</code></pre>
<pre><code class="language-lisp">(match #(1 2 3 4)
((vector* _ x _)
x))
;; -&gt; 2 : soft match.
</code></pre>
<pre><code>&lt;vector-pattern&gt; : vector | simple-vector
bit-vector | simple-bit-vector
string | simple-string
base-string | simple-base-string | sequence
(&lt;vector-pattern&gt; &amp;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/>
&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>