deploy
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.1 Propositional Logic</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.1 Propositional Logic</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>As we get ready to write larger and more complex programs, we’re going to take a pause on programming to study formal mathematical logic. You might wonder what logic has to do with software development. As we’ll see over the course of this chapter, a firm understanding of logic allows us to precisely identify, define, and write <em>boolean expressions</em> and use them in our programs.</p>
|
||||
<p>It might seem counter-intuitive to spend a whole chapter on logic, as <code>bool</code> is the simplest data type in Python. But writing boolean expressions that correctly capture definitions and conditions in a given problem domain can be tricky as these definitions and conditions grow in complexity. It will turn out to be very useful to have a formal mathematical language—logic—to express these complex boolean expressions before turning them into code.</p>
|
||||
<h2 id="propositions">Propositions</h2>
|
||||
<p>We will start our study in this chapter with <em>propositional logic</em>, an elementary system of logic that is a crucial building block underlying other, more expressive systems of logic that we will need in this course.</p>
|
||||
<div class="definition" data-terms="proposition, propositional variable, propositional/logical operator, propositional formula">
|
||||
<p>A <strong>proposition</strong> is a statement that is either True or False. Examples of propositions are:</p>
|
||||
<ul>
|
||||
<li><span class="math inline">\(2+4 =6\)</span></li>
|
||||
<li><span class="math inline">\(3-5 > 0\)</span></li>
|
||||
<li>Every even integer greater than <span class="math inline">\(2\)</span> is the sum of two prime numbers.</li>
|
||||
<li>Python’s implementation of <code>list.sort</code> is correct on every input list.</li>
|
||||
</ul>
|
||||
<p>We use <strong>propositional variables</strong> to represent propositions; by convention, propositional variable names are lowercase letters starting at <span class="math inline">\(p\)</span>.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> The concept of a propositional variable is different from other forms of variables you have seen before, and even ones that we will see later in this chapter. Here’s a rule of thumb: if you read an expression involving a propositional variable <span class="math inline">\(p\)</span>, you should be able to replace <span class="math inline">\(p\)</span> with the statement “CSC110 is cool” and still have the expression make sense.</span></p>
|
||||
<p>A <strong>propositional/logical operator</strong> is an operator whose arguments must all be either True or False. Finally, a <strong>propositional formula</strong> is an expression that is built up from propositional variables by applying these operators.</p>
|
||||
</div>
|
||||
<p>In the following sections, we describe the various operators we will use in this course. It is important to keep in mind when reading that these operators inform both the <em>structure</em> of formulas (what they look like) as well as the <em>truth value</em> of these formulas (what they mean: whether the formula is True or False based on the truth values of the individual propositional variables).</p>
|
||||
<h2 id="the-basic-operators-not-and-or">The basic operators <strong>NOT</strong>, <strong>AND</strong>, <strong>OR</strong></h2>
|
||||
<p>We have seen these operators earlier when discussing different types of data. The fact that Python has specific keywords dedicated to these operators should at least hint that they are frequently used. Here, we spend some time introducing the operators more formally and developing our first truth tables.</p>
|
||||
<div class="margintable">
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: center;"><span class="math inline">\(p\)</span></th>
|
||||
<th style="text-align: right;"><span class="math inline">\(\lnot p\)</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: right;">True</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: right;">False</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>The unary operator <strong>NOT</strong> (also called “negation”) is denoted by the symbol <span class="math inline">\(\lnot\)</span>. It negates the truth value of its input. So if <span class="math inline">\(p\)</span> is True, then <span class="math inline">\(\lnot p\)</span> is False, and vice versa. This is shown in the <em>truth table</em> at the side. In Python, we use the <code>not</code> keyword to represent this operation.</p>
|
||||
<div class="margintable">
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: center;"><span class="math inline">\(p\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(q\)</span></th>
|
||||
<th style="text-align: right;"><span class="math inline">\(p \land q\)</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: right;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: right;">False</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: right;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: right;">True</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>The binary operator <strong>AND</strong> (also called “conjunction”) is denoted by the symbol <span class="math inline">\(\land\)</span>. It returns True when both its arguments are True. In Python, we use the <code>and</code> keyword to represent this operation.</p>
|
||||
<p>The binary operator <strong>OR</strong> (also called “disjunction”) is denoted by the symbol <span class="math inline">\(\lor\)</span>, and returns True if one or both of its arguments are True. In Python, we use the <code>or</code> keyword to represent this operation.</p>
|
||||
<div class="margintable">
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: center;"><span class="math inline">\(p\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(q\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(p \lor q\)</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>The truth tables for <strong>AND</strong> and <strong>NOT</strong> agree with the popular English usage of the terms; however, the operator <strong>OR</strong> may seem somewhat different from your intuition, because the word “or” has two different meanings to most English speakers. Consider the English statement “You can have cake or ice cream.” From a nutritionist, this might be an <em>exclusive or</em>: you can have cake or you can have ice cream, but not both. But from a kindly relative at a family reunion, this might be an <em>inclusive or</em>: you can have both cake and ice cream if you want! The study of mathematical logic is meant to eliminate the ambiguity by picking one meaning of <strong>OR</strong> and sticking with it. In our case, we will always use <strong>OR</strong> to mean the <em>inclusive or</em>, as illustrated in the last row of its truth table.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote">The symbol <span class="math inline">\(\oplus\)</span> is often used to represent the <em>exclusive or</em> operator, but we will not use it in this course.</span> This is also the behaviour of the <code>or</code> operator in Python, which evaluates to <code>True</code> when both of its operands are <code>True</code>.</p>
|
||||
<p><strong>AND</strong> and <strong>OR</strong> are similar in that they are both <em>binary</em> operators on propositional variables. However, the distinction between <strong>AND</strong> and <strong>OR</strong> is very important. Consider for example a rental agreement that reads “first and last months’ rent <em>and</em> a $1000 deposit” versus a rental agreement that reads “first and last months’ rent <em>or</em> a $1000 deposit.” The second contract is fulfilled with much less money down than the first contract.</p>
|
||||
<h2 id="the-implication-operator">The implication operator</h2>
|
||||
<p>One of the most subtle and powerful relationships between two propositions is <em>implication</em>, which is represented by the symbol <span class="math inline">\(\Rightarrow\)</span>. The implication <span class="math inline">\(p \Rightarrow q\)</span> asserts that whenever <span class="math inline">\(p\)</span> is True, <span class="math inline">\(q\)</span> must also be True. An example of logical implication in English is the statement: “If you push that button, then the fire alarm will go off.”<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> In some contexts, we think of logical implication as the temporal relationship that <span class="math inline">\(q\)</span> is inevitable if <span class="math inline">\(p\)</span> occurs. But this is <em>not</em> always the case! Be careful not to confuse implication with causation.</span> Implications are so important that the parts have been given names. The statement <span class="math inline">\(p\)</span> is called the <em>hypothesis</em> of the implication and the statement <span class="math inline">\(q\)</span> is called the <em>conclusion</em> of the implication.</p>
|
||||
<div class="margintable">
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: center;"><span class="math inline">\(p\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(q\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(p \Rightarrow q\)</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>How should the truth table be defined for <span class="math inline">\(p \Rightarrow q\)</span>? First, when both <span class="math inline">\(p\)</span> and <span class="math inline">\(q\)</span> are True, then <span class="math inline">\(p \Rightarrow q\)</span> should be True, since when <span class="math inline">\(p\)</span> occurs, <span class="math inline">\(q\)</span> also occurs. Similarly, it is clear that when <span class="math inline">\(p\)</span> is True and <span class="math inline">\(q\)</span> is False, then <span class="math inline">\(p \Rightarrow q\)</span> is False (since then <span class="math inline">\(q\)</span> is not inevitably True when <span class="math inline">\(p\)</span> is True). But what about the other two cases, when <span class="math inline">\(p\)</span> is False and <span class="math inline">\(q\)</span> is either True or False? This is another case where our intuition from both English language it a little unclear. Perhaps somewhat surprisingly, in both of these remaining cases, we will still define <span class="math inline">\(p \Rightarrow q\)</span> to be True.</p>
|
||||
<p>The two cases when <span class="math inline">\(p\)</span> is False but <span class="math inline">\(p \Rightarrow q\)</span> is True are called the <strong>vacuous truth</strong> cases. How do we justify this assignment of truth values? The key intuition is that because the statement doesn’t say anything about whether or not <span class="math inline">\(q\)</span> should occur when <span class="math inline">\(p\)</span> is False, it cannot be disproven when <span class="math inline">\(p\)</span> is False. In our example above, if the alarm button is <em>not</em> pushed, then the statement is not saying anything about whether or not the fire alarm will go off. It is entirely consistent with this statement that if the button is not pushed, the fire alarm can still go off, or may not go off.</p>
|
||||
<p>The formula <span class="math inline">\(p \Rightarrow q\)</span> has two equivalent<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote">Here, “equivalent” means that the two formulas have the same truth values; for any setting of their propositional variables to True and False, the formulas will either both be True or both be False.</span> formulas which are often useful. To make this concrete, we’ll use our example “If you are a Pittsburgh Pens fan, then you are not a Flyers fan” from the introduction.</p>
|
||||
<p>The following two formulas are equivalent to <span class="math inline">\(p \Rightarrow q\)</span>:</p>
|
||||
<ul>
|
||||
<li><p><span class="math inline">\(\lnot p \lor q\)</span>. On our example: “You are not a Pittsburgh Pens fan, or you are not a Flyers fan.” This makes use of the vacuous truth cases of implication, in that if <span class="math inline">\(p\)</span> is False then <span class="math inline">\(p \Rightarrow q\)</span> is True, and if <span class="math inline">\(p\)</span> is True then <span class="math inline">\(q\)</span> must be True as well.</p></li>
|
||||
<li><p><span class="math inline">\(\lnot q \Rightarrow \lnot p\)</span>. On our example: “If you <em>are</em> a Flyers fan, then you are <em>not</em> a Pittsburgh Pens fan.” Intuitively, this says that if <span class="math inline">\(q\)</span> doesn’t occur, then <span class="math inline">\(p\)</span> cannot have occurred either.</p>
|
||||
<p>This equivalent formula is in fact so common that we give it a special name: the <strong>contrapositive</strong> of the implication <span class="math inline">\(p \Rightarrow q\)</span>.</p></li>
|
||||
</ul>
|
||||
<p>There is one more related formula that we will discuss before moving on. If we take <span class="math inline">\(p \Rightarrow q\)</span> and switch the hypothesis and conclusion, we obtain the implication <span class="math inline">\(q \Rightarrow p\)</span>, which is called the <strong>converse</strong> of the original implication.</p>
|
||||
<p>Unlike the two formulas in the list above, the converse of an implication is <em>not</em> logically equivalent to the original implication. Consider the statement “If you can solve any problem in this course, then you will get an A.” Its converse is “If you will get an A, then you can solve any problem in this course.” These two statements certainly don’t mean the same thing!</p>
|
||||
<p>In Python, there is no operator or keyword that represents implication directly. If you do want to express an implication as a Python expression, we can use the first equivalent form from above, writing <span class="math inline">\(p \Rightarrow q\)</span> as <span class="math inline">\(\lnot p \lor q\)</span>. This is less common in Python programs; however, implication has other uses in manipulating data and expressing algorithms that we’ll explore later in this course.</p>
|
||||
<h2 id="biconditional-if-and-only-if">Biconditional (“if and only if”)</h2>
|
||||
<p>The final logical operator that we will consider is the <em>biconditional</em>, denoted by <span class="math inline">\(p \Leftrightarrow q\)</span>. This operator returns True when the implication <span class="math inline">\(p \Rightarrow q\)</span> and its converse <span class="math inline">\(q \Rightarrow p\)</span> are both True.</p>
|
||||
<div class="margintable">
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: center;"><span class="math inline">\(p\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(q\)</span></th>
|
||||
<th style="text-align: center;"><span class="math inline">\(p \Leftrightarrow q\)</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
<td style="text-align: center;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
<td style="text-align: center;">True</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>In other words, <span class="math inline">\(p \Leftrightarrow q\)</span> is an abbreviation for <span class="math inline">\((p \Rightarrow q) \land (q \Rightarrow p)\)</span>. A nice way of thinking about the biconditional is that it asserts that its two arguments have the <em>same</em> truth value.</p>
|
||||
<p>While we could use the natural translation of <span class="math inline">\(\Rightarrow\)</span> and <span class="math inline">\(\land\)</span> into English to also translate <span class="math inline">\(\Leftrightarrow\)</span>, the result is a little clunky: <span class="math inline">\(p \Leftrightarrow q\)</span> becomes “if <span class="math inline">\(p\)</span> then <span class="math inline">\(q\)</span>, and if <span class="math inline">\(q\)</span> then <span class="math inline">\(p\)</span>.” Instead, we often shorten this using a quite nice turn of phrase: “<span class="math inline">\(p\)</span> if and only if <span class="math inline">\(q\)</span>,” which is abbreviated to “<span class="math inline">\(p\)</span> iff <span class="math inline">\(q\)</span>.”</p>
|
||||
<p>In Python, we don’t need a separate operator to represent <span class="math inline">\(\Leftrightarrow\)</span>, since we can simply use <code>==</code> to determine whether two boolean values are the same!</p>
|
||||
<h2 id="summary">Summary</h2>
|
||||
<p>We have now seen all five propositional operators that we will use in this course. Now is an excellent time to review these and make sure you understand the notation, meaning, and English words used to indicate each one.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: center;">operator</th>
|
||||
<th style="text-align: center;">notation</th>
|
||||
<th style="text-align: center;">English</th>
|
||||
<th style="text-align: center;">Python operation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;"><strong>NOT</strong></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(\lnot p\)</span></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p\)</span> is not true</td>
|
||||
<td style="text-align: center;"><code>not p</code></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;"><strong>AND</strong></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p \land q\)</span></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p\)</span> and <span class="math inline">\(q\)</span></td>
|
||||
<td style="text-align: center;"><code>p and q</code></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;"><strong>OR</strong></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p \lor q\)</span></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p\)</span> or <span class="math inline">\(q\)</span> (or both!)</td>
|
||||
<td style="text-align: center;"><code>p or q</code></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">implication</td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p \Rightarrow q\)</span></td>
|
||||
<td style="text-align: center;">if <span class="math inline">\(p\)</span>, then <span class="math inline">\(q\)</span></td>
|
||||
<td style="text-align: center;"><code>not p or q</code></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">biconditional</td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p \Leftrightarrow q\)</span></td>
|
||||
<td style="text-align: center;"><span class="math inline">\(p\)</span> if and only if <span class="math inline">\(q\)</span></td>
|
||||
<td style="text-align: center;"><code>p == q</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--div exercise>
|
||||
<div questions data-series="chapter1">
|
||||
\item
|
||||
A **tautology** is a formula that is True for every possible assignment of values
|
||||
to its propositional variables.
|
||||
Decide if each of the following propositional formulas are tautologies.
|
||||
|
||||
a) $((p \Rightarrow q) \land (p \Rightarrow r)) \Leftrightarrow (p \Rightarrow (q \land r))$
|
||||
b) $(p \Rightarrow q) \Leftrightarrow (\lnot p \lor q)$
|
||||
c) $(\lnot (p \lor q)) \Leftrightarrow (\lnot p \land \lnot q)$
|
||||
</div>
|
||||
</div-->
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,270 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.2 Predicate Logic</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.2 Predicate Logic</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>While propositional logic is a good starting point, most interesting statements in mathematics contain variables over domains larger than simply <span class="math inline">\(\{\TRUE, \FALSE\}\)</span>. For example, the statement “<span class="math inline">\(x\)</span> is a power of 2” is not a proposition because its truth value depends on the value of <span class="math inline">\(x\)</span>. It is only after we <em>substitute</em> a value for <span class="math inline">\(x\)</span> that we may determine whether the resulting statement is True or False. For example, if <span class="math inline">\(x = 8\)</span>, then the statement becomes “8 is a power of 2”, which is True. But if <span class="math inline">\(x = 7\)</span>, then the statement becomes “7 is a power of 2”, which is False.</p>
|
||||
<p>A statement whose truth value depends on one or more variables from any set is a <strong>predicate</strong>: a function whose codomain is <span class="math inline">\(\{\TRUE, \FALSE\}\)</span>. We typically use uppercase letters starting from <span class="math inline">\(P\)</span> to represent predicates, differentiating them from propositional variables. For example, if <span class="math inline">\(P(x)\)</span> is defined to be the statement “<span class="math inline">\(x\)</span> is a power of <span class="math inline">\(2\)</span>”, then <span class="math inline">\(P(8)\)</span> is True and <span class="math inline">\(P(7)\)</span> is False. Thus a predicate is like a proposition except that it contains one or more variables; when we substitute particular values for the variables, we obtain a proposition.</p>
|
||||
<p>As with all functions, predicates can depend on more than one variable. For example, if we define the predicate <span class="math inline">\(Q(x,y)\)</span> to mean “<span class="math inline">\(x^2 = y\)</span>,” then <span class="math inline">\(Q(5,25)\)</span> is True since <span class="math inline">\(5^2 = 25\)</span>, but <span class="math inline">\(Q(5,24)\)</span> is False.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote">Just as how common arithmetic operators like <span class="math inline">\(+\)</span> are really binary functions, the common comparison operators like <span class="math inline">\(=\)</span> and <span class="math inline">\(<\)</span> are <em>binary predicates</em>, taking two numbers and returning True or False.</span></p>
|
||||
<p>We usually define a predicate by giving the statement that involves the variables, e.g., “<span class="math inline">\(P(x)\)</span> is the statement ‘<span class="math inline">\(x\)</span> is a power of 2.’” However, there is another component which is crucial to the definition of a predicate: the domain that each of the predicate’s variable(s) belong to. You must always give the domain of a predicate as part of its definition. So we would complete the definition of <span class="math inline">\(P(x)\)</span> as follows:</p>
|
||||
<p><span class="math display">\[P(x): \text{``$x$ is a power of 2,'' where $x \in \N$.}\]</span></p>
|
||||
<h2 id="quantification-of-variables">Quantification of variables</h2>
|
||||
<p>Unlike propositional formulas, a predicate by itself does not have a truth value: as we discussed earlier, “<span class="math inline">\(x\)</span> is a power of 2” is neither True nor False, since we don’t know the value of <span class="math inline">\(x\)</span>. We have seen one way to obtain a truth value in substituting a concrete element of the predicate’s domain for its input, e.g., setting <span class="math inline">\(x = 8\)</span> in the statement “<span class="math inline">\(x\)</span> is a power of 2,” which is now True.</p>
|
||||
<p>However, we often don’t care about whether a specific value satisfies a predicate, but rather some aggregation of the predicate’s truth values over <em>all</em> elements of its domain. For example, the statement “every real number <span class="math inline">\(x\)</span> satisfies the inequality <span class="math inline">\(x^2 - 2x + 1 \geq 0\)</span>” doesn’t make a claim about a specific real number like 5 or <span class="math inline">\(\pi\)</span>, but rather <em>all possible</em> values of <span class="math inline">\(x\)</span>!</p>
|
||||
<p>There are two types of “truth value aggregation” we want to express; each type is represented by a <strong>quantifier</strong> that modifies a predicate by specifying how a certain variable should be interpreted.</p>
|
||||
<h3 id="existential-quantifier">Existential quantifier</h3>
|
||||
<div class="definition" data-terms="existential quantifier">
|
||||
<p>The <strong>existential quantifier</strong> is written as <span class="math inline">\(\exists\)</span>, and represents the concept of “<em>there exists</em> an element in the domain that satisfies the given predicate.”</p>
|
||||
</div>
|
||||
<div class="example">
|
||||
<p>For example, the statement <span class="math inline">\(\exists x \in \N,~ x \geq 0\)</span> can be translated as “there exists a natural number <span class="math inline">\(x\)</span> that is greater than or equal to zero.” This statement is True since (for example) when <span class="math inline">\(x=1\)</span>, we know that <span class="math inline">\(x \geq 0\)</span>.</p>
|
||||
<p>Note that there are many more natural numbers that are greater than or equal to <span class="math inline">\(0\)</span>. The existential quantifier says only that there has to be <em>at least one</em> element of the domain satisfying the predicate, but it doesn’t say exactly how many elements do so.</p>
|
||||
</div>
|
||||
<p>One should think of <span class="math inline">\(\exists x \in S\)</span> as an abbreviation for a big <strong>OR</strong> that runs through all possible values for <span class="math inline">\(x\)</span> from the domain <span class="math inline">\(S\)</span>. For the previous example, we can expand it by substituting all possible natural numbers for <span class="math inline">\(x\)</span>:<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote">In this case, the <strong>OR</strong> expression is technically infinite, since there are infinitely many natural numbers.</span> <span class="math display">\[(0 \geq 0) \lor (1 \geq 0) \lor (2 \geq 0) \lor (3 \geq 0) \lor \cdots\]</span></p>
|
||||
<h3 id="universal-quantifier">Universal quantifier</h3>
|
||||
<div class="definition" data-terms="universal quantifier">
|
||||
<p>The <strong>universal quantifier</strong> is written as <span class="math inline">\(\forall\)</span>, and represents the concept that “<em>every</em> element in the domain satisfies the given predicate.”</p>
|
||||
</div>
|
||||
<div class="example">
|
||||
<p>For example, the statement <span class="math inline">\(\forall x \in \N,~ x \geq 0\)</span> can be translated as “every natural number <span class="math inline">\(x\)</span> is greater than or equal to zero.” This statement is True since the smallest natural number is zero itself. However, the statement <span class="math inline">\(\forall x \in \N,~ x \geq 10\)</span> is False, since not every natural number is greater than or equal to 10.</p>
|
||||
</div>
|
||||
<p>One should think of <span class="math inline">\(\forall x \in S\)</span> as an abbreviation for a big <strong>AND</strong> that runs through all possible values of <span class="math inline">\(x\)</span> from <span class="math inline">\(S\)</span>. Thus, <span class="math inline">\(\forall x \in \N,~ x \geq 0\)</span> is the same as <span class="math display">\[(0 \geq 0) \land (1 \geq 0) \land (2 \geq 0) \land (3 \geq 0) \land \cdots\]</span></p>
|
||||
<div class="example">
|
||||
<p>Let us look at a simple example of these quantifiers. Suppose we define <span class="math inline">\(Loves(a,b)\)</span> to be a binary predicate that is <span class="math inline">\(\TRUE\)</span> whenever person <span class="math inline">\(a\)</span> loves person <span class="math inline">\(b\)</span>.</p>
|
||||
<!--
|
||||
<div class="marginfigure">
|
||||
\begin{tikzpicture}
|
||||
|
||||
% A
|
||||
\node [left] at (0,0) {Ella};
|
||||
\node [left] at (0,1) {Patrick};
|
||||
\node [left] at (0,2) {Malena};
|
||||
\node [left] at (0,3) {Breanna};
|
||||
|
||||
% B
|
||||
\node [right] at (2,0) {Laura};
|
||||
\node [right] at (2,1) {Stanley};
|
||||
\node [right] at (2,2) {Thelonious};
|
||||
\node [right] at (2,3) {Sophia};
|
||||
% Ella
|
||||
\draw [red, ultra thick, ->] (0,0) -- (2,0); % Ella loves Laura
|
||||
\draw [red, ultra thick, ->] (0,0) -- (2,1); % Ella loves Stanley
|
||||
% Patrick
|
||||
\draw [green, ultra thick, ->] (0,1) -- (2,1); % Patrick loves Stanley
|
||||
% Malena
|
||||
\draw [blue, ultra thick, ->] (0,2) -- (2,0); % Malena loves Laura
|
||||
\draw [blue, ultra thick, ->] (0,2) -- (2,1); % Malena loves Stanley
|
||||
\draw [blue, ultra thick, ->] (0,2) -- (2,2); % Malena loves Thelonious
|
||||
% Breanna
|
||||
\draw [black, ultra thick, ->] (0,3) -- (2,1); % Breanna loves Stanley
|
||||
\draw [black, ultra thick, ->] (0,3) -- (2,2); % Breanna loves Thelonious
|
||||
\end{tikzpicture}
|
||||
|
||||
</div> -->
|
||||
<p>For example, the diagram below defines the relation “Loves” for two collections of people: <span class="math inline">\(A\)</span> = {Ella, Patrick, Malena, Breanna}, and <span class="math inline">\(B\)</span> = {Laura, Stanley, Thelonious, Sophia}. A line between two people indicates that the person on the left loves the person on the right.</p>
|
||||
<div style="text-align: center">
|
||||
<p><img src="images/loves.png" width="400" alt="Loves diagram" /><br />
|
||||
</p>
|
||||
</div>
|
||||
<p>Consider the following statements.</p>
|
||||
<ul>
|
||||
<li><span class="math inline">\(\exists a \in A,~Loves(a, \text{Thelonious})\)</span>, which means “there exists someone in <span class="math inline">\(A\)</span> who loves Thelonious.” This is True since Malena loves Thelonious.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote">We could also have said here that Breanna loves Thelonious.</span></li>
|
||||
<li><span class="math inline">\(\exists a \in A,~Loves(a, \text{Sophia})\)</span>, which means “there exists someone in <span class="math inline">\(A\)</span> who loves Sophia.” This is False since no one loves Sophia.</li>
|
||||
<li><span class="math inline">\(\forall a \in A,~Loves(a, \text{Stanley})\)</span>, which means “every person in <span class="math inline">\(A\)</span> loves Stanley.” This is True, since all four people in <span class="math inline">\(A\)</span> love Stanley.</li>
|
||||
<li><span class="math inline">\(\forall a \in A,~Loves(a, \text{Thelonious})\)</span>, which means “every person in <span class="math inline">\(A\)</span> loves Thelonious.” This is False, since Ella does not love Thelonius.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h2 id="python-built-ins-any-and-all">Python built-ins: <code>any</code> and <code>all</code></h2>
|
||||
<p>In Python, the built-in function <code>any</code> allows us to represent logical statements using the existential quantifier. The function <code>any</code> takes a collection of boolean values and returns <code>True</code> when there exists a <code>True</code> value in the collection:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="op">>>></span> <span class="bu">any</span>([<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>])</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a><span class="va">True</span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a><span class="op">>>></span> <span class="bu">any</span>([]) <span class="co"># An empty collection has no True values!</span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a><span class="va">False</span></span></code></pre></div>
|
||||
<p>This might not seem useful by itself, but remember that we can use comprehensions to transform one collection of data into another. For example, suppose are given a set of strings <span class="math inline">\(S\)</span> and wish to determine whether any of them start with the letter <code>'D'</code>. In predicate logic, we could write this as the statement <span class="math inline">\(\exists s \in S,~ s[0] = \text{‘D'}\)</span>. And in Python, we could do the following:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="op">>>></span> strings <span class="op">=</span> [<span class="st">'Hello'</span>, <span class="st">'Goodbye'</span>, <span class="st">'David'</span>]</span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="op">>>></span> <span class="bu">any</span>([s[<span class="dv">0</span>] <span class="op">==</span> <span class="st">'D'</span> <span class="cf">for</span> s <span class="kw">in</span> strings])</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>This example serves to highlight several elegant parallels between our mathematical statement and equivalent Python expression:</p>
|
||||
<ul>
|
||||
<li><span class="math inline">\(\exists\)</span> corresponds to calling the <code>any</code> function</li>
|
||||
<li><span class="math inline">\(s \in S\)</span> corresponds to <code>for s in strings</code><label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> The naming conventions are a bit different, however: in mathematics, we tend to represent collections using capital letters, whereas in Python all variables are lower-case words.</span></li>
|
||||
<li><span class="math inline">\(s[0] = \text{‘D'}\)</span> corresponds to <code>s[0] == 'D'</code></li>
|
||||
</ul>
|
||||
<p>Similar to <code>any</code>, Python includes another built-in function <code>all</code> that can be used as a universal quantifier. The <code>all</code> function is given a collection of values and evaluates to <code>True</code> when every element has the value <code>True</code>. For example, if we wanted to express <span class="math inline">\(\forall s \in S,~ s[0] = \text{‘D'}\)</span> in Python, we could write:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">>>></span> strings <span class="op">=</span> [<span class="st">'Hello'</span>, <span class="st">'Goodbye'</span>, <span class="st">'David'</span>]</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="op">>>></span> <span class="bu">all</span>([s[<span class="dv">0</span>] <span class="op">==</span> <span class="st">'D'</span> <span class="cf">for</span> s <span class="kw">in</span> strings])</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a><span class="va">False</span></span></code></pre></div>
|
||||
<p>Of course, Python is more limited than mathematics because there are limits on the size of the collections, and so we cannot easily express existential statement quantified over infinite domains like <span class="math inline">\(\N\)</span> or <span class="math inline">\(\R\)</span>. We’ll discuss this in more detail in a later section.</p>
|
||||
<h2 id="writing-sentences-in-predicate-logic">Writing sentences in predicate logic</h2>
|
||||
<p>Now that we have introduced the existential and universal quantifiers, we have a complete set of tools needed to represent all statements we’ll see in this course. A general formula in predicate logic is built up using the existential and universal quantifiers, the propositional operators <span class="math inline">\(\lnot\)</span>, <span class="math inline">\(\land\)</span>, <span class="math inline">\(\lor\)</span>, <span class="math inline">\(\Rightarrow\)</span>, and <span class="math inline">\(\Leftrightarrow\)</span>, and arbitrary predicates. To ensure that the formula has a fixed truth value, we will require every variable in the formula to be quantified.<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote">Other texts will often refer to quantified variables as <em>bound variables</em>, and unquantified variables as <em>free variables</em>.</span> We call a formula with no unquantified variables a <strong>sentence</strong>. So for example, the formula <span class="math display">\[\forall x \in \N,~ x^2 > y\]</span> is not a sentence: even though <span class="math inline">\(x\)</span> is quantified, <span class="math inline">\(y\)</span> is not, and so we cannot determine the truth value of this formula. If we quantify <span class="math inline">\(y\)</span> as well, we get a sentence: <span class="math display">\[\forall x, y \in \N,~ x^2 > y.\]</span></p>
|
||||
<p>However, don’t confuse a formula being a sentence with a formula being True! As we’ll see repeatedly throughout the course, it is quite possible to express both True and False sentences, and part of our job will be to determine whether a given sentence is True or False, and to prove it.</p>
|
||||
<h3 id="commas-avoid-them">Commas: avoid them!</h3>
|
||||
<p>Here is a common question from students who are first learning symbolic logic: “does the comma mean ‘and’ or ‘then’?” As we discussed at the start of the course, we study to predicate logic to provide us with an unambiguous way of representing ideas. The English language is filled with ambiguities that can make it hard to express even relatively simple ideas, much less the complex definitions and concepts used in many fields of computer science. We have seen one example of this ambiguity in the English word “or,” which can be inclusive or exlusive, and often requires additional words of clarification to make precise. In everyday communication, these ambiguous aspects of the English language contribute to its richness of expression. But in a technical context, ambiguity is undesirable: it is much more useful to limit the possible meanings to make them unambiguous and precise.</p>
|
||||
<p>There is another, more insidious example of ambiguity with which you are probably more familiar: the <em>comma</em>, a tiny, easily-glazed-over symbol that people often infuse with different meanings. Consider the following statements:</p>
|
||||
<ol type="1">
|
||||
<li>If it rains tomorrow, I’ll be sad.</li>
|
||||
<li>David is cool, Toniann is cool.</li>
|
||||
</ol>
|
||||
<p>Our intuitions tell us very different things about what the commas mean in each case. In the first, the comma means <em>then</em>, separating the hypothesis and conclusion of an implication. But in the second, the comma is used to mean <em>and</em>, the implicit joining of two separate sentences.<label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote">Grammar-savvy folks will recognize this as a <em>comma splice</em>, which is often frowned upon but informs our reading nonetheless.</span> The fact that we are all fluent in English means that our prior intuition hides the ambiguity in this symbol, but it is quite obvious when we put this into the more unfamiliar context of predicate logic, as in the formula: <span class="math display">\[P(x), Q(x)\]</span></p>
|
||||
<p>This, of course, is where the confusion lies, and is the origin of the question posed at the beginning of this section. Because of this ambiguity, <strong>never use the comma to connect propositions</strong>. We already have a rich enough set of symbols—including <span class="math inline">\(\land\)</span> and <span class="math inline">\(\Rightarrow\)</span>—that we do not need another one that is ambiguous and adds nothing new!</p>
|
||||
<p>That said, keep in mind that commas do have two valid uses in predicate formulas:</p>
|
||||
<ul>
|
||||
<li>immediately after a variable quantification, or separating two variables with the same quantification</li>
|
||||
<li>separating arguments to a predicate</li>
|
||||
</ul>
|
||||
<p>You can see both of these usages illustrated below, but please do remember that these are the <em>only</em> valid places for the comma within symbolic notation! <span class="math display">\[\forall x, y \in \N,~ \forall z \in \R,~ P(x, y) \Rightarrow Q(x, y, z)\]</span></p>
|
||||
<h2 id="manipulating-negation">Manipulating negation</h2>
|
||||
<p>We have already seen some equivalences among logical formulas, such as the equivalence of <span class="math inline">\(p \Rightarrow q\)</span> and <span class="math inline">\(\lnot p \lor q\)</span>. While there are many such equivalences, the only other major type that is important for this course are the ones used to simplify negated formulas. Taking the negation of a statement is extremely common, because often when we are trying to decide if a statement is True, it is useful to know exactly what its negation means and decide whether the negation is more plausible than the original.</p>
|
||||
<p>Given any formula, we can state its negation simply by preceding it by a <span class="math inline">\(\lnot\)</span> symbol: <span class="math display">\[\lnot \big( \forall x \in \N,~ \exists y \in \N,~ x \geq 5 \lor x^2 - y \geq 30 \big).\]</span> However, such a statement is rather hard to understand if you try to transliterate each part separately: “Not for every natural number <span class="math inline">\(x\)</span>, there exists a natural number <span class="math inline">\(y\)</span>, such that <span class="math inline">\(x\)</span> is greater than or equal to <span class="math inline">\(5\)</span> or <span class="math inline">\(x^2 - y\)</span> is greater than or equal to 30.”</p>
|
||||
<p>Instead, given a formula using negations, we apply some <em>simplification rules</em> to “push” the negation symbol to the right, closer the to individual predicates. Each simplification rule shows how to “move the negation inside” by one step, giving a pair of equivalent formulas, one with the negation applied to one of the logical operator or quantifiers, and one where the negation is applied to inner subexpressions.</p>
|
||||
<ul>
|
||||
<li><span class="math inline">\(\lnot (\lnot p)\)</span> becomes <span class="math inline">\(p\)</span>.</li>
|
||||
<li><span class="math inline">\(\lnot (p \lor q)\)</span> becomes <span class="math inline">\((\lnot p) \land (\lnot q)\)</span>.<label for="sn-6" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-6" class="margin-toggle"/><span class="sidenote">The negation rules for AND and OR are known as <em>deMorgan’s laws</em>.</span></li>
|
||||
<li><span class="math inline">\(\lnot (p \land q)\)</span> becomes <span class="math inline">\((\lnot p) \lor (\lnot q)\)</span>.</li>
|
||||
<li><span class="math inline">\(\lnot (p \Rightarrow q)\)</span> becomes <span class="math inline">\(p \land (\lnot q)\)</span>.<label for="sn-7" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-7" class="margin-toggle"/><span class="sidenote">Since <span class="math inline">\(p \Rightarrow q\)</span> is equivalent to <span class="math inline">\(\lnot p \lor q\)</span>.</span></li>
|
||||
<li><span class="math inline">\(\lnot (p \Leftrightarrow q)\)</span> becomes <span class="math inline">\((p \land (\lnot q)) \lor ((\lnot p) \land q))\)</span>.</li>
|
||||
<li><span class="math inline">\(\lnot (\exists x \in S,~ P(x))\)</span> becomes <span class="math inline">\(\forall x \in S,~ \lnot P(x)\)</span>.</li>
|
||||
<li><span class="math inline">\(\lnot (\forall x \in S,~ P(x))\)</span> becomes <span class="math inline">\(\exists x \in S,~ \lnot P(x)\)</span>.</li>
|
||||
</ul>
|
||||
<p>It is usually easy to remember the simplification rules for <span class="math inline">\(\land\)</span>, <span class="math inline">\(\lor\)</span>, <span class="math inline">\(\forall\)</span>, and <span class="math inline">\(\exists\)</span>, since you simply “flip” them when moving the negation inside. The intuition for the negation of <span class="math inline">\(p \Rightarrow q\)</span> is that there is only one case where this is False: when <span class="math inline">\(p\)</span> has occurred but <span class="math inline">\(q\)</span> does not. The intuition for the negation of <span class="math inline">\(p \Leftrightarrow q\)</span> is to remember that <span class="math inline">\(\Leftrightarrow\)</span> can be replaced with “have the same truth value,” so the negation is “have different truth values.”</p>
|
||||
<p>What about the quantifiers? Consider a statement of the form <span class="math inline">\(\lnot (\exists x \in S,~ P(x))\)</span>, which says “there does not exist an element <span class="math inline">\(x\)</span> of <span class="math inline">\(S\)</span> that satisfies <span class="math inline">\(P\)</span>.” The only way this could be true is for every element of <span class="math inline">\(S\)</span> to <em>not</em> satisfy <span class="math inline">\(P\)</span>: “every element <span class="math inline">\(x\)</span> of <span class="math inline">\(S\)</span> does not satisfy <span class="math inline">\(P\)</span>.” A similar line of reasoning applies to <span class="math inline">\(\lnot (\forall x \in S,~ P(x))\)</span>.</p>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,174 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.3 Filtering Collections</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.3 Filtering Collections</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>Now we’re going to take a look at one of the most common steps in expressing statements in predicate logic and in processing large collections of data. At first glance these two might not appear that related, after going through this section you should be able to appreciate this elegant connection between predicate logic and data processing.</p>
|
||||
<h2 id="expressing-conditions-in-predicate-logic">Expressing conditions in predicate logic</h2>
|
||||
<p>We saw in the last section that the universal quantifier <span class="math inline">\(\forall\)</span> is used to express a statement of the form “every element of set <span class="math inline">\(S\)</span> satisfies ____”. This works well when we use a predefined set for <span class="math inline">\(S\)</span> (like the numeric sets <span class="math inline">\(\N\)</span> or <span class="math inline">\(\R\)</span>), but does not work well when we want to narrow the scope of our statement to a smaller set.</p>
|
||||
<p>For example, consider the following statement: “Every natural number <span class="math inline">\(n\)</span> greater than 3 satisfies the inequality <span class="math inline">\(n^2 + n \geq 20\)</span>.” The phrase “greater than 3” is a <em>condition</em> that modifies the statement, limiting the original domain of <span class="math inline">\(n\)</span> (the natural numbers) to a smaller subset (the natural numbers greater than 3).</p>
|
||||
<p>There are two ways we can represent such conditions in predicate logic. The first is to define a new set; for example, we could define a set <span class="math inline">\(S_1 = \{n \mid n \in \N \text{ and } n > 3\}\)</span>, and then simply write <span class="math inline">\(\forall n \in S_1,~ n^2 + n \geq 20\)</span>.</p>
|
||||
<p>The second approach is to use an implication to express the condition. To see how this works, first we can rewrite the original statement using an “if … then …” structure as follows: “For every natural number <span class="math inline">\(n\)</span>, if <span class="math inline">\(n\)</span> is greater than 3 then <span class="math inline">\(n\)</span> satisfies the inequality <span class="math inline">\(n^2 + n \geq 20\)</span>.” We can translate this into predicate logic as <span class="math inline">\(\forall n \in \N,~ n > 3 \Rightarrow n^2 + n \geq 20\)</span>.</p>
|
||||
<p>This works because the <span class="math inline">\(n > 3 \Rightarrow\)</span> has a filtering effect, due to the <em>vacuous truth</em> case of implication. For the values <span class="math inline">\(n \in \{0, 1, 2\}\)</span>, the hypothesis of the implication, <span class="math inline">\(n > 3\)</span> is False, and so for these values the implication itself is True. And then since the overall statement is universally quantified, these vacuous truth cases don’t affect the truth value of the statement.</p>
|
||||
<p>The “forall-implies” structure is one of the most common forms of statements we’ll encounter in this course. They arise naturally any time a statement is universally quantified, but there are conditions that limit the domain that the statement applies to.</p>
|
||||
<h2 id="filtering-collections-in-python">Filtering collections in Python</h2>
|
||||
<p>Now let’s turn our attention back to Python. Last chapter, we learned about several aggregation functions (like <code>sum</code>, <code>max</code>), and we’ve just learned about two more, <code>any</code> and <code>all</code>. Sometimes, however, we want to limit the scope of one of these functions to certain values in the input collection. For example, “find the sum of only the even numbers in a collection of numbers”, or “find the length of the longest string in a collection that starts with a <code>'D'</code>”. For these problems, we can quickly identify which aggregation function is necessary, but the problem is in choosing the right argument to pass in.</p>
|
||||
<p>This is where filtering appears. In programming, a <strong>filter operation</strong> is an operation that takes a collection of data and returns a new collection consisting of the elements in the original collection that satisfy some predicate (which can vary from one filter operation to the next).</p>
|
||||
<p>There are different ways of accomplishing a filter operation in Python. The simplest one builds on what we’ve learned so far by adding a syntactic variation to comprehensions. We’ll use as our example a set comprehension here, but what we’ll discuss applies to list and dictionary comprehensions as well.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a>{<span class="op"><</span>expression<span class="op">></span> <span class="cf">for</span> <span class="op"><</span>variable<span class="op">></span> <span class="kw">in</span> <span class="op"><</span>collection<span class="op">></span> <span class="cf">if</span> <span class="op"><</span>condition<span class="op">></span>}</span></code></pre></div>
|
||||
<p>The new part, <code>if <condition></code>, is a boolean expression involving the <code><variable></code>. This form of set comprehension behaves the same way as the ones we studied last chapter, except that <code><expression></code> only gets evaluated for the values of the variable that make the condition evaluate to <code>True</code>. Here are some examples to illustrate this:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="op">>>></span> numbers <span class="op">=</span> {<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>} <span class="co"># Initial collection</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="op">>>></span> {n <span class="cf">for</span> n <span class="kw">in</span> numbers <span class="cf">if</span> n <span class="op">></span> <span class="dv">3</span>} <span class="co"># Pure filtering: only keep elements > 3</span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a>{<span class="dv">4</span>, <span class="dv">5</span>}</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a><span class="op">>>></span> {n <span class="op">*</span> n <span class="cf">for</span> n <span class="kw">in</span> numbers <span class="cf">if</span> n <span class="op">></span> <span class="dv">3</span>} <span class="co"># Filtering with a data transformation</span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a>{<span class="dv">16</span>, <span class="dv">25</span>}</span></code></pre></div>
|
||||
<p>By combining these filtering comprehensions with aggregation functions, we can now achieve our goal of limiting the scope of an aggregation.</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">>>></span> numbers <span class="op">=</span> {<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>}</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="op">>>></span> <span class="bu">sum</span>({n <span class="cf">for</span> n <span class="kw">in</span> numbers <span class="cf">if</span> n <span class="op">%</span> <span class="dv">2</span> <span class="op">==</span> <span class="dv">0</span>}) <span class="co"># Sum of only the even numbers</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a><span class="dv">6</span></span></code></pre></div>
|
||||
<p>The keyword <code>if</code> used in this syntax for filtering comprehensions is directly connected to our use of implication above. Just as we used the hypothesis <span class="math inline">\(n > 3 \Rightarrow\)</span> to limit the scope of the universal quantifier to a subset of the natural numbers, here we use <code>if n % 2 == 0</code> to limit the scope of the <code>sum</code> to just a subset of <code>numbers</code>.</p>
|
||||
<p>Our final example in this section should make this connection even more explicit. Here’s how we could translate the statement <span class="math inline">\(\forall n \in S,~ n > 3 \Rightarrow n^2 + n \geq 20\)</span> into a Python expression:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="op">>>></span> numbers <span class="op">=</span> {<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>, <span class="dv">6</span>, <span class="dv">7</span>, <span class="dv">8</span>}</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><span class="op">>>></span> <span class="bu">all</span>({n <span class="op">**</span> <span class="dv">2</span> <span class="op">+</span> n <span class="op">>=</span> <span class="dv">20</span> <span class="cf">for</span> n <span class="kw">in</span> numbers <span class="cf">if</span> n <span class="op">></span> <span class="dv">3</span>})</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a><span class="va">True</span></span></code></pre></div>
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.4 Conditional Execution</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.4 Conditional Execution</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>So far, all of the function bodies we’ve written have consisted of a sequence of statements that always execute one after the other.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> This kind of code block is sometimes called a “straight line program”, since the statements form a linear path from one to the next.</span> But sometimes we want to execute a statement or block of statements only some of the time, based on some condition.</p>
|
||||
<p>This is similar to the implication operator we saw when discussing propositional logic. The implication <span class="math inline">\(p \Rightarrow q\)</span> states that whenever <span class="math inline">\(p\)</span> is True, <span class="math inline">\(q\)</span> must also be True. In Python, what we would like to express is something of the form “Whenever <span class="math inline">\(p\)</span> is True, then the block of code <code>block1</code> must be executed”. To do so, we’ll introduce a new type of Python statement that play a role analogous to <span class="math inline">\(\Rightarrow\)</span> in propositional logic.</p>
|
||||
<h2 id="the-if-statement">The if statement</h2>
|
||||
<p>Python uses the <strong>if statement</strong> to express conditional execution of code. An if statement is a <strong>compound statement</strong>, meaning it contains other statements within it.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Analogously, a expression like <code>3 + 4</code> is a <em>compound expression</em>, since it consists of smaller expressions (<code>3</code> and <code>4</code>).</span> Here is our first syntax for an if statement:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="cf">if</span> <span class="op"><</span>condition<span class="op">></span>:</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> <span class="op"><</span>statement<span class="op">></span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a> ...</span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a><span class="cf">else</span>:</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> <span class="op"><</span>statement<span class="op">></span></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> ...</span></code></pre></div>
|
||||
<p>The if statement uses two keywords, <code>if</code> and <code>else</code>.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> Careful: we saw the <code>if</code> keyword used earlier to express conditions in comprehensions. The use of <code>if</code> here is logically similar, but quite different in how Python interprets it.</span> The <code><condition></code> following <code>if</code> must be an expression that evaluates to a boolean, called the <strong>if condition</strong>. This expression plays a role analogous to the hypothesis of an implication.</p>
|
||||
<p>The statements on the lines after the <code>if</code> and <code>else</code> are indented to indicate that they are part of the <code>if</code> statement, similar to how a function docstring and body are indented relative to the function header. We call the statements under the <code>if</code> the <strong>if branch</strong> and the statements under the <code>else</code> the <strong>else branch</strong>.</p>
|
||||
<p>When an <code>if</code> statement is executed, the following happens:</p>
|
||||
<ol type="1">
|
||||
<li>First, the if condition is evaluated, producing a boolean value.</li>
|
||||
<li>If the condition evaluates to <code>True</code>, then the statements in the if branch are executed. If the condition evaluates to <code>False</code>, then the statements in the else branch are executed instead.</li>
|
||||
</ol>
|
||||
<p>Let us consider an example. Suppose Toronto Pearson Airport (YYZ) has hired us to develop some software. The first feature they want is to show their clients if a flight is on time or delayed. The airport will provide us with both the time a flight is scheduled to depart and an estimated departure time based on the plane’s current GPS location. Our task is to report a status (as a string) to display a string. Here is the function header and docstring:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> get_status(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">"""Return the flight status for the given scheduled and estimated departure times.</span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a><span class="co"> The times are given as integers between 0 and 23 inclusive, representing</span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> the hour of the day.</span></span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a><span class="co"> The status is either 'On time' or 'Delayed'.</span></span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a><span class="co"> >>> get_status(10, 10)</span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a><span class="co"> 'On time'</span></span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a><span class="co"> >>> get_status(10, 12)</span></span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a><span class="co"> 'Delayed'</span></span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a><span class="co"> """</span></span></code></pre></div>
|
||||
<p>Now, if we only needed to calculate a <code>bool</code> for whether the flight is delayed, this function would be very straightforward: simply return <code>estimated <= scheduled</code>, i.e., whether the estimated departure time is before or at the scheduled departure time. Boolean expressions like this are often useful first steps in implementing functions to determine different “cases” of inputs, but they aren’t the only step.</p>
|
||||
<p>Instead, we use if statements to execute different code based on these cases. Here’s our implementation of <code>get_status</code>:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> get_status(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">"""..."""</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">if</span> estimated <span class="op"><=</span> scheduled:</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a> <span class="cf">return</span> <span class="st">'On time'</span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a> <span class="cf">return</span> <span class="st">'Delayed'</span></span></code></pre></div>
|
||||
<p>Our if statement uses the boolean expression we identified earlier (<code>estimated <= scheduled</code>) to trigger different <code>return</code> statements to return the correct string.</p>
|
||||
<h3 id="a-simple-control-flow-diagram">A simple control flow diagram</h3>
|
||||
<p>One useful tool for understanding if statements is drawing <em>control flow diagrams</em> to visualize the order in which statements execute. For example, here is a simple diagram for our <code>get_status</code> function above:</p>
|
||||
<p><img src="images/get-status.png" alt="get status control flow diagram" /><br />
|
||||
</p>
|
||||
<p>An <code>if</code> statement introduces a “fork in path” of a function’s control flow, and this is why we use the term <em>branch</em> for each of the <code>if</code> and <code>else</code> blocks of code.</p>
|
||||
<h2 id="code-with-more-than-two-cases">Code with more than two cases</h2>
|
||||
<p>Now suppose Toronto Pearson Airport has changed the requirements of our feature. They’ve noticed that whenever a flight is delayed by more than four hours, the airline cancels the flight. They would like our <code>get_status</code> function to accommodate this change, so that the set of possible outputs is now <code>{'On time', 'Delayed', 'Cancelled'}</code>.</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> get_status_v2(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">"""Return the flight status for the given scheduled and estimated departure times.</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a><span class="co"> The times are given as integers between 0 and 23 inclusive, representing</span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a><span class="co"> the hour of the day.</span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a><span class="co"> The status is 'On time', 'Delayed', or 'Cancelled'.</span></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a><span class="co"> >>> get_status_v2(10, 10)</span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a><span class="co"> 'On time'</span></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a><span class="co"> >>> get_status_v2(10, 12)</span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a><span class="co"> 'Delayed'</span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a><span class="co"> >>> get_status_v2(10, 15)</span></span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a><span class="co"> 'Cancelled'</span></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a><span class="co"> """</span></span></code></pre></div>
|
||||
<p>Let’s consider what’s changed between this version and our previous one. If the estimated time is before the scheduled time, nothing’s changed, and <code>'On time'</code> should still be returned. But when the estimated time is after the schedule time, we now need to distinguish between two separate subcases, based on the difference in time. We can express these subcases using nested if statements, i.e., one if statement contained in a branch of another:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> get_status_v2(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">"""..."""</span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> <span class="cf">if</span> estimated <span class="op"><=</span> scheduled:</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> <span class="cf">return</span> <span class="st">'On time'</span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> <span class="cf">if</span> estimated <span class="op">-</span> scheduled <span class="op"><=</span> <span class="dv">4</span>:</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> <span class="cf">return</span> <span class="st">'Delayed'</span></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a> <span class="cf">return</span> <span class="st">'Cancelled'</span></span></code></pre></div>
|
||||
<p>This function body is correct, but just like with expressions, excessive nesting of statements can make code difficult to read and understand. So instead of using a nested if statement, we’ll introduce a new form of if statement that makes use of the <code>elif</code> keyword, which is short for “else if”.</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="cf">if</span> <span class="op"><</span>condition1<span class="op">></span>:</span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a> <span class="op"><</span>statement<span class="op">></span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> ...</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a><span class="cf">elif</span> <span class="op"><</span>condition2<span class="op">></span>:</span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> <span class="op"><</span>statement<span class="op">></span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a> ...</span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a>... <span class="co"># [any number of elif conditions and branches]</span></span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a><span class="cf">else</span>:</span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a> <span class="op"><</span>statement<span class="op">></span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> ...</span></code></pre></div>
|
||||
<p>When this form of if statement is executed, the following happens.</p>
|
||||
<ol type="1">
|
||||
<li>First, the if condition (<code><condition1></code>) is evaluated, producing a boolean value.</li>
|
||||
<li>If the condition evaluates to <code>True</code>, then the statements in the if branch are executed. If the condition evaluates to <code>False</code>, then next elif condition is evaluated, producing a boolean.</li>
|
||||
<li>If that condition evaluates to <code>True</code>, then the statements in that elif branch are executed. If that condition evaluates to <code>False</code>, then the next elif condition is evaluated. This step repeats until either one of the elif conditions evaluate to <code>True</code>, or all of the elif conditions have evaluated to <code>False</code>.</li>
|
||||
<li>If neither the if condition nor any of the elif conditions evaluate to <code>True</code>, then the else branch executes.</li>
|
||||
</ol>
|
||||
<p>Here is how we can use <code>elif</code> to rewrite <code>get_status</code> without nested if statements.</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> get_status_v3(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">"""Return the flight status for the given scheduled and estimated departure times.</span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> The times are given as integers between 0 and 23 inclusive, representing</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> the hour of the day.</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a></span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a><span class="co"> The status is 'On time', 'Delayed', or 'Cancelled'.</span></span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a></span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a><span class="co"> >>> get_status_v3(10, 10)</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a><span class="co"> 'On time'</span></span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a><span class="co"> >>> get_status_v3(10, 12)</span></span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a><span class="co"> 'Delayed'</span></span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a><span class="co"> >>> get_status_v3(10, 15)</span></span>
|
||||
<span id="cb7-14"><a href="#cb7-14"></a><span class="co"> 'Cancelled'</span></span>
|
||||
<span id="cb7-15"><a href="#cb7-15"></a><span class="co"> """</span></span>
|
||||
<span id="cb7-16"><a href="#cb7-16"></a> <span class="cf">if</span> estimated <span class="op"><=</span> scheduled:</span>
|
||||
<span id="cb7-17"><a href="#cb7-17"></a> <span class="cf">return</span> <span class="st">'On time'</span></span>
|
||||
<span id="cb7-18"><a href="#cb7-18"></a> <span class="cf">elif</span> estimated <span class="op">-</span> scheduled <span class="op"><=</span> <span class="dv">4</span>:</span>
|
||||
<span id="cb7-19"><a href="#cb7-19"></a> <span class="cf">return</span> <span class="st">'Delayed'</span></span>
|
||||
<span id="cb7-20"><a href="#cb7-20"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb7-21"><a href="#cb7-21"></a> <span class="cf">return</span> <span class="st">'Cancelled'</span></span></code></pre></div>
|
||||
<p>This code is logically equivalent to the previous version, but it’s easier to read because there’s no more nesting! Now, it is clear exactly what are the three possible branches of execution for this function.</p>
|
||||
<!-- TODO: Add control flow diagram for get_status_v2 and get_status_v3 -->
|
||||
<h2 id="testing-all-the-branches">Testing all the branches</h2>
|
||||
<p>Adding branching to our control flow makes our functions more complex, and so we need to pay attention to how we test our code. With functions that contain if statements, any one particular input we give can only test one possible execution path, so we need to design our unit tests so that each possible execution path is used at least once. This form of test design is called <strong>white box</strong> testing, because we “see through the box” and therefore can design tests based on the source code itself. In contrast, <strong>black box</strong> testing are tests created without any knowledge of the source code (so no knowledge of the different paths the code can take).</p>
|
||||
<p>In our doctests for <code>get_status_v3</code>, we chose three different examples, each corresponding to a different possible case of the if statement. This was pretty straightforward because the code is relatively simple, but we’ll study later example of more complex control flow where it won’t be so simple to design test cases to cover each branch. In fact, the percentage of lines of program code that are executed when a set of tests for that program is called <strong>code coverage</strong>, and is a metric used to assess the quality of tests. While a set of tests may strive for 100% code coverage, this does not always occur as our programs grow in complexity. The concept of code coverage and other metrics used to evaluate tests is something we’ll only touch on in this course, but in future courses you’ll learn about this in more detail and even use some automated tools for calculating these metrics.<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> In particular, even though code coverage is a commonly used metric, it is also criticized for giving a false sense of quality of a test suite. Just because all lines of code are executed at least once does not actually mean that the tests chosen cover all possible cases to consider for a program. We’ll see a simple example of this in the following section.</span></p>
|
||||
<h2 id="building-on-our-example">Building on our example</h2>
|
||||
<p>Toronto Pearson Airport is beginning to trust us with more data, and are requesting more complex features as a result. They now want us to write a function that determines how many flights are cancelled in a day. The airport will provide us with the data as a dictionary (i.e., <code>dict</code>), where the keys are unique flight numbers and the values for each flight number is a two-element list. The first element is the scheduled time and the second element is the estimated time. More succinctly, the data is a mapping of the form: <code>{ flight_number: [scheduled, estimated] }</code>.</p>
|
||||
<p>Unlike earlier, when our function input was only two integers, we are now working with a collection of data. Before we start trying to solve the problem, let’s create some example data in the Python console. Specifically, we’ll create a dictionary with values for three different Air Canada flight numbers.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">>>></span> flights <span class="op">=</span> {<span class="st">'AC110'</span>: [<span class="dv">10</span>, <span class="dv">12</span>], <span class="st">'AC321'</span>: [<span class="dv">12</span>, <span class="dv">19</span>], <span class="st">'AC999'</span>: [<span class="dv">1</span>, <span class="dv">1</span>]}</span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a><span class="op">>>></span> flights[<span class="st">'AC110'</span>]</span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a>[<span class="dv">10</span>, <span class="dv">12</span>]</span></code></pre></div>
|
||||
<p>We know that we can query the dictionary by providing an existing key. The value associated with a key is a list of integers, and we can index the list to retrieve those integers. Index 0 of the list refers to the flight number’s scheduled time, while index 1 refers to the estimated time. Let us call our <code>get_status_v3</code> function for flight <code>'AC110'</code>:</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="op">>>></span> flight_ac110 <span class="op">=</span> flights[<span class="st">'AC110'</span>]</span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a><span class="op">>>></span> get_status_v3(flight_ac110[<span class="dv">0</span>], flight_ac110[<span class="dv">1</span>])</span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a><span class="co">'Delayed'</span></span></code></pre></div>
|
||||
<p>We’re making great progress! Instead of specifying the flight number ourselves (i.e., <code>'AC110'</code>), we would instead like to substitute in different flight numbers based on the data we receive from the airport. We can do that using comprehensions. Let’s explore and see what we can get:</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="op">>>></span> {k <span class="cf">for</span> k <span class="kw">in</span> flights}</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a>{<span class="st">'AC999'</span>, <span class="st">'AC110'</span>, <span class="st">'AC321'</span>}</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a><span class="op">>>></span> {get_status_v3(flights[k][<span class="dv">0</span>], flights[k][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span> <span class="cf">for</span> k <span class="kw">in</span> flights}</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a>{<span class="va">False</span>, <span class="va">True</span>}</span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a><span class="op">>>></span> [get_status_v3(flights[k][<span class="dv">0</span>], flights[k][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span> <span class="cf">for</span> k <span class="kw">in</span> flights]</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a>[<span class="va">False</span>, <span class="va">True</span>, <span class="va">False</span>]</span></code></pre></div>
|
||||
<p>Our first set comprehension can get us the set of flight numbers, but that doesn’t tell us if the flight was cancelled or not. When we created our second set comprehension we could see that there was at least one flight cancelled. Remember that sets only contain unique elements, and this set consists of all possible boolean values. When we create a list comprehension, we can see that exactly one out of three flights were cancelled (there is one <code>True</code> value). But remember that the airport only wants to know how many flights were cancelled; a single integer value. Currently, we have a list of boolean values.</p>
|
||||
<p>Let us now try to combine the first set comprehension with the second, using the filtering we learned in the last section.</p>
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="op">>>></span> {k <span class="cf">for</span> k <span class="kw">in</span> flights <span class="cf">if</span> get_status_v3(flights[k][<span class="dv">0</span>], flights[k][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span>}</span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a>{<span class="st">'AC321'</span>}</span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a><span class="op">>>></span> [k <span class="cf">for</span> k <span class="kw">in</span> flights <span class="cf">if</span> get_status_v3(flights[k][<span class="dv">0</span>], flights[k][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span>]</span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a>[<span class="st">'AC321'</span>]</span></code></pre></div>
|
||||
<p>Excellent! We now have a set of flight numbers that were cancelled. To convert this into an integer, we can use the built-in <code>len</code> function on the set.<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote"> Something to think about: does it matter if we use the list or set comprehension here?</span> Let’s see what all this looks like in a function:</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> count_cancelled(flights: <span class="bu">dict</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">"""Return the number of cancelled flights for the given flight data.</span></span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a></span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a><span class="co"> flights is a dictionary where each key is a flight ID,</span></span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a><span class="co"> and whose corresponding value is a list of two numbers, where the first is</span></span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a><span class="co"> the scheduled departure time and the second is the estimated departure time.</span></span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a></span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a><span class="co"> >>> count_cancelled({'AC110': [10, 12], 'AC321': [12, 19], 'AC999': [1, 1]})</span></span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a><span class="co"> 1</span></span>
|
||||
<span id="cb12-10"><a href="#cb12-10"></a><span class="co"> """</span></span>
|
||||
<span id="cb12-11"><a href="#cb12-11"></a> cancelled_flights <span class="op">=</span> {k <span class="cf">for</span> k <span class="kw">in</span> flights</span>
|
||||
<span id="cb12-12"><a href="#cb12-12"></a> <span class="cf">if</span> get_status_v3(flights[k][<span class="dv">0</span>], flights[k][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span>}</span>
|
||||
<span id="cb12-13"><a href="#cb12-13"></a> <span class="cf">return</span> <span class="bu">len</span>(cancelled_flights)</span></code></pre></div>
|
||||
<p>Let’s review what we learned in this example:</p>
|
||||
<ul>
|
||||
<li>We can try to remember how we can use what we’ve learned by exploring in the Python console, well before starting to write the function. Here, we refreshed our memory on how we might look up values from dictionaries, index lists, call functions, create comprehensions, and filter collections.</li>
|
||||
<li>We can substitute in different values for a function’s input using comprehensions.</li>
|
||||
<li>We can reuse functions we’ve already created and tested (like <code>get_status_v3</code>) to help implement other functions.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,200 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.5 Simplifying If Statements</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.5 Simplifying If Statements</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>In the last section we introduced if statements, a powerful Python structure that allowed us to perform conditional execution of blocks of code. But as we’ll see again and again in this course, expressive power comes with a cost: as our toolkit gets larger and the programming language features we use get more advanced, our programs also get larger and more complex; harder to read and reason about.</p>
|
||||
<p>So every time we introduce a new part of the Python programming language, we’ll also take some time to discuss not just what it can do, but also how to use it in structured ways that minimize the complexity we create by using it, and how to reason about its behaviour formally using tools from mathematical logic.</p>
|
||||
<h2 id="computing-booleans-when-if-statements-arent-necessary">Computing booleans: when if statements aren’t necessary</h2>
|
||||
<p>As our first example, consider the following function:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> is_even(n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">"""Return whether n is even (divisible by 2)."""</span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a> <span class="cf">if</span> n <span class="op">%</span> <span class="dv">2</span> <span class="op">==</span> <span class="dv">0</span>:</span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a> <span class="cf">return</span> <span class="va">True</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> <span class="cf">return</span> <span class="va">False</span></span></code></pre></div>
|
||||
<p>When we first learn about if statements, it is tempting to use them whenever we think of different “cases” of inputs, like even vs. odd numbers in this example. But remember that if statements are fundamentally about taking boolean values and conditionally executing code (usually to generate other values). In cases where all we need is a boolean value, <em>it is often simpler to write an expression to calculate the value directly, rather than using if statements</em>.</p>
|
||||
<p>In our example, the if statement is redundant and can be simplified just by returning the value of the condition:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> is_even(n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">"""Return whether n is even (divisible by 2)."""</span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> <span class="cf">return</span> n <span class="op">%</span> <span class="dv">2</span> <span class="op">==</span> <span class="dv">0</span></span></code></pre></div>
|
||||
<p>Indeed, our earlier study of propositional logic should make us comfortable with the idea of treating booleans just like any other kind of value, and we should make full use of Python’s logical operators <code>and</code>, <code>or</code>, and <code>not</code> to combine them.</p>
|
||||
<p>Consider this more complex example with nested if statements:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode numberSource python numberLines"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> mystery(x: lst, y: lst) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a> <span class="cf">if</span> x <span class="op">==</span> []:</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">if</span> y <span class="op">==</span> []:</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a> <span class="cf">return</span> <span class="va">True</span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a> <span class="cf">return</span> <span class="va">False</span></span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb3-8"><a href="#cb3-8"></a> <span class="cf">if</span> y <span class="op">==</span> []:</span>
|
||||
<span id="cb3-9"><a href="#cb3-9"></a> <span class="cf">return</span> <span class="va">False</span></span>
|
||||
<span id="cb3-10"><a href="#cb3-10"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb3-11"><a href="#cb3-11"></a> <span class="cf">return</span> <span class="va">True</span></span></code></pre></div>
|
||||
<p>Here is a control flow diagram for this function, showing the four different possible execution paths.</p>
|
||||
<p><img src="images/mystery.png" alt="mystery function control flow diagram" /><br />
|
||||
</p>
|
||||
<p>To simplify this, we start with the first inner if statement on lines 3-6. This follows the same structure as our first example, and can be simplified to just <code>return y == []</code>.</p>
|
||||
<p>The second inner if statement on lines 8-11 follows a similar structure, except that now the boolean that’s returned is the negation of the if condition. So we can simplify this as <code>return not y == []</code>, which we can simplify further using the <code>!=</code> operator: <code>return y != []</code>.</p>
|
||||
<p>So now we have this simplification of the function body:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> mystery(x: lst, y: lst) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a> <span class="cf">if</span> x <span class="op">==</span> []:</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> <span class="cf">return</span> y <span class="op">==</span> []</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> <span class="cf">return</span> y <span class="op">!=</span> []</span></code></pre></div>
|
||||
<p>But now how do we simplify this further? The idea here is to focus on the possible ways that <code>mystery</code> could return <code>True</code>. The if statement divides the inputs into two cases: when <code>x == []</code> and the if branch executes, and when <code>x != []</code> and the else branch executes. In the first case, when <code>x == []</code>, <code>mystery</code> returns the value of <code>y == []</code>. So one case for <code>mystery</code> returning <code>True</code> is when <code>x == [] and y == []</code>. Similarly, in the second case, when <code>x != []</code>, <code>mystery</code> returns <code>y != []</code>, and so the other case for <code>mystery</code> returning <code>True</code> is <code>x != [] and y != []</code>.</p>
|
||||
<p>How should we combine these two cases? Because these are different cases, either one of them could occur, but we don’t expect both of them to occur (since <code>x == []</code> and <code>x != []</code> can’t both be true), and so we combine them using <code>or</code>:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> mystery(x: lst, y: lst) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a> <span class="cf">return</span> (x <span class="op">==</span> [] <span class="kw">and</span> y <span class="op">==</span> []) <span class="kw">or</span> (x <span class="op">!=</span> [] <span class="kw">and</span> y <span class="op">!=</span> [])</span></code></pre></div>
|
||||
<p>This simplification took a bit of work, but as a result we have a clearer picture of what this function does. We can illustrate this further by breaking up the nested expression using local variables with meaningful names.</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">def</span> mystery(x: lst, y: lst) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a> both_empty <span class="op">=</span> x <span class="op">==</span> [] <span class="kw">and</span> y <span class="op">==</span> []</span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> both_non_empty <span class="op">=</span> x <span class="op">!=</span> [] <span class="kw">and</span> y <span class="op">!=</span> []</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> <span class="cf">return</span> both_empty <span class="kw">or</span> both_non_empty</span></code></pre></div>
|
||||
<p>To check your understanding, try writing a docstring description for this function. You’ll probably find it at least a little easier to do for this version than the original. And while this is still a relatively small example, the same principle will often apply in the future, and so be on the lookout for if statements that you can simplify in this way.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> That said, this simplification won’t always apply or be appropriate, depending on the complexity of the branches of the statement. We’ll discuss this in more detail later.</span></p>
|
||||
<h2 id="using-if-statements">Using if statements</h2>
|
||||
<p><code>if</code> statements create branches in our code, allowing us to create more advanced functions. But more branches means more complexity because there are many possible paths that our function could take when called. To mitigate the complexity that comes with branching, we recommend two principles when working with if statements:</p>
|
||||
<ol type="1">
|
||||
<li>Prefer using a sequence of <code>elif</code>s rather than nested <code>if</code> statements. Overuse of nesting makes your code harder to understand, and can make the visual structure of your code more complex than necessary.</li>
|
||||
<li>Write your conditions from most specific to most general. Order matters for these conditions, since they are checked one at a time in top-down order.</li>
|
||||
</ol>
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,191 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.6 if __name__ == '__main__'</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.6 <code>if __name__ == '__main__'</code></h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>One small application of if statements that we’ve already taken for granted in this course is writing certain “boilerplate” code for running certain libraries on our file. For example, we saw in <a href="../02-functions/06-testing-functions-1.html">2.6 Testing Functions I: <code>doctest</code> and <code>pytest</code></a> that we add the following code to our Python file to run the doctests in that file:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">'__main__'</span>:</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> <span class="im">import</span> doctest</span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a> doctest.testmod()</span></code></pre></div>
|
||||
<p>Now that we’ve learned about if statements, we are ready to understand that first line, <code>if __name__ == '__main__'</code>.</p>
|
||||
<h2 id="import-statements-revisited">import statements revisited</h2>
|
||||
<p>In <a href="../02-functions/04-importing-modules.html">2.4 Importing Modules</a>, we learned that an import statement is an instruction to the Python interpreter to find a Python module with a specified name and run it. This allows the program that executes the import statement to access the functions and data types defined within that module.</p>
|
||||
<p>One consequence of this behaviour, though, is that by default <em>all</em> statements in the imported module are executed, not just function and data type definitions.</p>
|
||||
<p>For example, suppose we had the following file <code>useful.py</code>:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="co"># useful.py</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a><span class="kw">def</span> useful_function1(x: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> <span class="co">"""...</span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a></span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> >>> useful_function1(1)</span></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a><span class="co"> 110</span></span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a><span class="co"> """</span></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a> <span class="co"># Body omitted</span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a></span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a></span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a><span class="kw">def</span> useful_function2(s: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> <span class="co">"""...</span></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a></span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a><span class="co"> >>> useful_function1('Hello')</span></span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a><span class="co"> 'Hello David'</span></span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a><span class="co"> """</span></span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a> <span class="co"># Body omitted</span></span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a></span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a></span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a><span class="im">import</span> doctest</span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a>doctest.testmod()</span></code></pre></div>
|
||||
<p>Note that here, the code to run <code>doctest</code> is not indented inside an if statement. It turns out that we can still run this file in the Python console, and the doctests will be run. However, these statements will also be executed every time <code>useful.py</code> is imported by another Python program. In other words, any time another program writes <code>import useful</code>, the doctests inside <code>useful.py</code> will be run, even though the doctests are not relevant for a program that just wants to use <code>useful.py</code>!</p>
|
||||
<h2 id="enter-__name__">Enter <code>__name__</code></h2>
|
||||
<p>To fix this problem, the Python interpreter creates a special variable called <code>__name__</code> for each module when a program is run.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> Python uses the “double underscore” naming convention to denote special variable or function names. We’ll encounter a few more of these throughout the course.</span> By default, the <code>__name__</code> variable is set to the name of the module: the <code>__name__</code> of <code>useful.py</code> is <code>'useful'</code>, and the <code>__name__</code> attribute of <code>math</code> is <code>'math'</code>.</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">>>></span> <span class="im">import</span> math</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="op">>>></span> math.<span class="va">__name__</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a><span class="co">'math'</span></span></code></pre></div>
|
||||
<p>However, when you <em>run</em> a module (e.g., right-click and select “Run File in Python Console”), the Python interpreter overrides the default module <code>__name__</code> and instead sets it to the special string <code>'__main__'</code>. And so checking the <code>__name__</code> variable is a way to determine if the current module is being run, or whether it’s being imported by another module!</p>
|
||||
<p>When we write <code>if __name__ == '__main__'</code>, we are really saying, “Execute the following code if this module is being run, and ignore the following code if this module is being imported by another module”. The boolean expression <code>__name__ == '__main__'</code> evaluates to <code>True</code> in the former case, and <code>False</code> in the latter, and the conditional execution happens because of the if statement.</p>
|
||||
<h2 id="organizing-a-python-file">Organizing a Python file</h2>
|
||||
<p>We call this if branch (all the code under <code>if __name__ == '__main__'</code>) the module’s <strong>main block</strong>. Here are some important conventions to follow for organizing a Python file with a main block:</p>
|
||||
<ol type="1">
|
||||
<li>The only code that goes outside of the main block are import statements (<code>import ...</code>), constant definitions (<code>MY_CONSTANT = ...</code>), function definitions (<code>def ...</code>), and data type definitions (<code>class ...</code>), which we will see in the next chapter.</li>
|
||||
<li>Other code, like code for running <code>doctest</code> or <code>pytest</code>, goes inside the main block so that it is only executed when the module is run, and not when it is imported.</li>
|
||||
<li>The main block goes at the bottom of the module.</li>
|
||||
</ol>
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,319 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.7 Function Specifications</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.7 Function Specifications</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>One of the most central questions in software development is, “How do we know that the software we write is correct?” Certainly, writing test cases will ensure that our functions produce the expected output for specific situations. But as our programs increase in complexity, how confident can we be that our test cases are sufficient?</p>
|
||||
<h2 id="function-specifications-and-correctness">Function specifications and correctness</h2>
|
||||
<p>Before we address this question, we will formalize what it means for a program to be correct in the first place. Because functions are the primary way we organize programs, we’ll focus on what it means for an individual function to be correct.</p>
|
||||
<p>A <strong>specification</strong> for a function consists of two parts:</p>
|
||||
<ol type="1">
|
||||
<li>A description of what values the function takes as valid inputs. We can represent this description as a set of predicates, where a valid input must satisfy all these predicates. We call these predicates the <strong>preconditions</strong> of the function.</li>
|
||||
<li>A description of what the function returns/does, in terms of its inputs.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> For now, all of our Python functions only return values, and do nothing else. Later on in the course, we’ll study other kinds of function behaviour that could be included in a specification.</span> We can represent this description as a set of predicates as well, that must all be satisfied by the return value of the function. We call these predicates the <strong>postconditions</strong> of the function.</li>
|
||||
</ol>
|
||||
<p>With these two parts, a function’s specification defines what we expect the function to do. The job of an implementation of the function is to provide the Python code in the function body that meets this specification. We say that a function implementation is <strong>correct</strong> when the following holds: <em>For all inputs that satisfy the specification’s preconditions, the function implementation’s return value satisfies the specification’s postconditions.</em></p>
|
||||
<p>A function specification acts as a contract or agreement between the person who implements the function and the person who calls the function. For the person implementing the function, their responsibility is to make sure their code correctly returns or does what the specification says. When writing this code, they do not need to worry about exactly how the function is called and <em>assume</em> that the function’s input is always valid.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> So in fact, we have already seen several preconditions in this course. Every time we had a function description that said “assume X about the input(s)”, that was a precondition.</span> For the person calling the function, their responsibility is to make sure they call the function with valid inputs. When they make this call, they do not need to worry about exactly how the function is implemented and <em>assume</em> that the function works correctly.</p>
|
||||
<p>The concept of a function specification is a very powerful one, as it spreads the responsibility of function correctness across two parties that do their parts separately—as long as they both know what the function specification is. As a result, these specifications must be very precise. Outside of software, lawyers are hired to draft and review contracts to make sure that they are defensible in the eyes of the law. Similarly, programmers must behave as lawyers when designing software to write ironclad contracts that leave no ambiguity in what is expected of the user or how the software will behave. In this section, we introduce some new tools and terminology that can help our functions be more explicit in their requirements and behaviour.</p>
|
||||
<h2 id="simple-specifications">Simple specifications</h2>
|
||||
<p>Even though we haven’t formally introduced the notion of a function specification until this section, you’ve been writing specifications all along simply by following the Function Design Recipe. Let’s take a look at an early example:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> is_even(n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">"""Return whether n is even.</span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a><span class="co"> >>> is_even(1)</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a><span class="co"> False</span></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a><span class="co"> >>> is_even(2)</span></span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a><span class="co"> True</span></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a><span class="co"> """</span></span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> <span class="co"># Body omitted.</span></span></code></pre></div>
|
||||
<p>Here, the <em>type contract</em> and <em>description</em> actually form a complete specification of this function’s behaviour:</p>
|
||||
<ol type="1">
|
||||
<li>The type annotation of the parameter <code>n</code> tells us that the valid inputs to <code>is_even</code> are <code>int</code> values. The type annotation <code>int</code> is itself a <em>precondition</em> of the function.</li>
|
||||
<li>Similarly, the type annotation for the return value tells us that the function will always return a <code>bool</code>. In addition, the description “Return whether n is even.” specifies the relationship between the function’s return value and its input.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> The doctest examples aid understanding, but are not strictly required to specify what this function does.</span> The function description and return type annotation specify the <em>postconditions</em> of the function.</li>
|
||||
</ol>
|
||||
<p>From this alone, we know what it means for this function to be implemented correctly, even if we can’t see the implementation.</p>
|
||||
<blockquote>
|
||||
<p><code>is_even</code> is implemented correctly when <em>for all <code>int</code>s <code>n</code>, <code>is_even(n)</code> returns a <code>bool</code> that is <code>True</code> when <code>n</code> is even, and <code>False</code> when <code>n</code> is not even</em>.</p>
|
||||
</blockquote>
|
||||
<p>For example, suppose David has implemented this function. Mario loads this function implementation into the Python console and calls it:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="op">>>></span> is_even(<span class="dv">4</span>)</span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="va">False</span></span></code></pre></div>
|
||||
<p>In this case, <code>4</code> is an <code>int</code>, so Mario held up his end of the contract when he called the function. But the <code>False</code> return value is inconsistent with the function description, and so we know there must be an error in the implementation—David is at fault, not Mario.</p>
|
||||
<p>Suppose David fixes his implementation, and asks Mario to try another call. Mario types in:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">>>></span> is_even(<span class="dv">4</span>)</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>Okay pretty good, and now Mario tries:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="op">>>></span> is_even([<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>])</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a>Traceback (most recent call last):</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> File <span class="st">"<stdin>"</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op"><</span>module<span class="op">></span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> File <span class="st">"<stdin>"</span>, line <span class="dv">2</span>, <span class="kw">in</span> is_even</span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a><span class="pp">TypeError</span>: unsupported operand <span class="bu">type</span>(s) <span class="cf">for</span> <span class="op">%</span>: <span class="st">'list'</span> <span class="kw">and</span> <span class="st">'int'</span></span></code></pre></div>
|
||||
<p>In this case, the function did not produce a return value but rather an error (i.e., <code>TypeError</code>). Is David at fault again? <em>No!</em> Mario violated the function’s precondition by passing in a <code>list</code> rather than an <code>int</code>, and so he should have no expectation that <code>is_even</code> will meet its postcondition. Therefore, Mario (the caller of the function) caused the error.</p>
|
||||
<h2 id="preconditions-in-general">Preconditions in general</h2>
|
||||
<p>All parameter type annotations are preconditions for a function. But often these type annotations are not precise enough to specify the exact set of valid inputs. Consider this function:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a><span class="co"> >>> max_length({'Hello', 'Mario', 'David Liu'})</span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a><span class="co"> 9</span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
|
||||
<p>What happens when the set is empty? Let’s try it out in the console:</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="op">>>></span> empty_set <span class="op">=</span> <span class="bu">set</span>()</span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a><span class="op">>>></span> max_length(empty_set)</span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a>Traceback (most recent call last):</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> File <span class="st">"<input>"</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op"><</span>module<span class="op">></span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> File <span class="st">"<input>"</span>, line <span class="dv">7</span>, <span class="kw">in</span> max_length</span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a><span class="pp">ValueError</span>: <span class="bu">max</span>() arg <span class="kw">is</span> an empty sequence</span></code></pre></div>
|
||||
<p>We’ve obtained an error, rather than an <code>int</code>; this makes logical sense, because it is impossible to find the maximum value in a set that contains no values at all. But from a formal function specification sense, who is to blame: the function’s caller or the function’s implementer?</p>
|
||||
<p>As it stands, the implementer is at fault because the only description of “valid inputs” given is the type annotation <code>set</code>; the empty set is still a <code>set</code>. So we need to update the specification to rule out this possibility, but how?<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> You may recall that we’ve been adding extra “assumptions” on inputs for programming exercises in this course for the past few weeks already. What we’re learning here is how to formalize these assumptions into function docstrings.</span> We encountered this issue in <a href="03-filtering-collections.html">3.3 Filtering Collections</a>, when we wanted to restrict a statement to apply to a subset of our domain. Here we’re doing the same thing: making the set of valid function inputs more specific, because we only want to guarantee our implementation works correctly on those inputs. We add a precondition to the function docstring as follows:</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> - len(strings) > 0</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
|
||||
<p>Whenever possible, we’ll express these general preconditions as valid Python expressions involving the function’s parameters.<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote"> Sometimes we’ll encounter a precondition that is extremely complex, in which case you can write them in English.</span> In English, we would say that the full specification of <code>max_length</code>’s valid inputs is “<code>strings</code> is a <code>set</code>, and <code>len(strings) > 0</code>”. As functions get more complex, we can add additional preconditions by listing them under the header <code>Preconditions:</code> in the docstring. <strong>A function input is valid when it satisfies the type annotations and <em>all</em> general precondition expressions</strong>.</p>
|
||||
<p>Note that adding the precondition to the docstring does not change the behaviour of the function. If an empty set is passed into the function by the user, the function will still produce the <code>ValueError</code> we saw above. However, now that the precondition has been documented in the function specification, if we call <code>max_length(empty_set)</code>, we know that the error is entirely our fault because we violated a precondition.</p>
|
||||
<h3 id="checking-preconditions-automatically-with-python_ta">Checking preconditions automatically with python_ta</h3>
|
||||
<p>While our previous example illustrates how to document preconditions as part of a function specification, it has one drawback: it relies on whoever is calling the function to read the documentation! Of course, reading documentation is an important skill for any computer scientist, but despite our best intentions we sometimes miss things. It would be nice if we could turn our preconditions into executable Python code so that the Python interpreter checks them every time we call the function.</p>
|
||||
<p>One way to do this is to use an <code>assert</code> statement, just like we do in unit tests. Because we’ve written the precondition as a Python expression, we can convert this to an assertion by copy-and-pasting it at the top of the function body.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a></span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a><span class="co"> - len(strings) > 0</span></span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a> <span class="cf">assert</span> <span class="bu">len</span>(strings) <span class="op">></span> <span class="dv">0</span>, <span class="st">'Precondition violated: max_length called on an empty set.'</span></span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
|
||||
<p>Now, the precondition is checked every time the function is called, with a meaningful error message when the precondition is violated:</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="op">>>></span> empty_set <span class="op">=</span> <span class="bu">set</span>()</span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a><span class="op">>>></span> max_length(empty_set)</span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a>Traceback (most recent call last):</span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a> File <span class="st">"<input>"</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op"><</span>module<span class="op">></span></span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> File <span class="st">"<input>"</span>, line <span class="dv">7</span>, <span class="kw">in</span> max_length</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a><span class="pp">AssertionError</span>: Precondition violated: max_length called on an empty <span class="bu">set</span>.</span></code></pre></div>
|
||||
<p>However, this approach is annoying and error-prone. First, we have to duplicate the precondition in two places. And second, we have increased the size of the function body with extra code. The <code>python_ta</code> library we use in this course has a way to automatically check preconditions for all functions in a given file. Here is an example:</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a></span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a><span class="co"> - len(strings) > 0</span></span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a></span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a></span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">'__main__'</span>:</span>
|
||||
<span id="cb10-11"><a href="#cb10-11"></a> <span class="im">import</span> python_ta.contracts</span>
|
||||
<span id="cb10-12"><a href="#cb10-12"></a> python_ta.contracts.DEBUG_CONTRACTS <span class="op">=</span> <span class="va">False</span> <span class="co"># Disable contract debug messages</span></span>
|
||||
<span id="cb10-13"><a href="#cb10-13"></a> python_ta.contracts.check_all_contracts()</span>
|
||||
<span id="cb10-14"><a href="#cb10-14"></a></span>
|
||||
<span id="cb10-15"><a href="#cb10-15"></a> max_length(<span class="bu">set</span>())</span></code></pre></div>
|
||||
<p>Notice that we’ve kept the function docstring the same, but removed the assertion. The function we call, <code>python_ta.contracts.check_all_contracts</code>, modifies our <code>max_length</code> function. That is, <code>python_ta</code> takes the function’s type contract and the preconditions it finds in the function docstring, and causes the function to check these preconditions every time the function is called! Let’s see what happens when we run this file:</p>
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a>Traceback (most recent call last):</span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a>...</span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a><span class="pp">AssertionError</span>: max_length precondition <span class="st">"len(strings) > 0"</span> violated <span class="cf">for</span> arguments {<span class="st">'strings'</span>: <span class="bu">set</span>()}.</span></code></pre></div>
|
||||
<p>Pretty cool! We’ll be using <code>check_all_contracts</code> for the rest of this course to help us make sure we’re sticking to the specifications we’ve written in our function header and docstrings when we call our functions. Moreover, <code>check_all_contracts</code> checks the return type of each function, so it’ll also work as a check when we’re implementing our functions to make sure the return value is of the correct type.</p>
|
||||
<h2 id="preconditions-as-assumptions-and-restrictions">Preconditions as assumptions and restrictions</h2>
|
||||
<p>Preconditions allow the implementer of a function to specify assumptions about the function’s inputs, and so simplify the work of the implementer. On the other hand, preconditions place restrictions on the user of the function; the onus is on them to respect these preconditions every time the function is called. This often increases the complexity of the code that calls the function. For example, in our <code>max_length</code> function, the calling code might need an if statement to first check whether a set is empty before passing it to <code>max_length</code>.</p>
|
||||
<p>When confronted with an “invalid input”, there is another strategy other than simply ruling out the invalid input with a precondition: explicitly defining some alternate function behaviour for this input. Here is another way we could define <code>max_length</code>:</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a></span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a><span class="co"> Return 0 if strings is empty.</span></span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a><span class="co"> """</span></span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a> <span class="cf">if</span> strings <span class="op">==</span> <span class="bu">set</span>():</span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a> <span class="cf">return</span> <span class="dv">0</span></span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
|
||||
<p>Here, we picked a reasonable default value for <code>max_length</code> when given an empty set,<label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote"> This is very similar to how we define empty sums and products by a mathematical convention.</span> and then handled that as an explicit case in our implementation by using an if statement. Our function implementation is more complex than before, but now another person can call our function on an empty set without producing an error:</p>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="op">>>></span> empty_set <span class="op">=</span> <span class="bu">set</span>()</span>
|
||||
<span id="cb13-2"><a href="#cb13-2"></a><span class="op">>>></span> max_length(empty_set)</span>
|
||||
<span id="cb13-3"><a href="#cb13-3"></a><span class="dv">0</span></span></code></pre></div>
|
||||
<p>You’re probably wondering: is this version of <code>max_length</code> better or worse than our original one with the precondition? This version resulted in a longer description and function body, but it also removed a possible error we might encounter when calling the function. On the other hand, is 0 really a “reasonable” value for the behaviour of this function? Because this is ultimately a design decision, there is no clear “right answer”—there are always trade-offs to be made. Rather than sticking with a particular rule (i.e., “always/never use preconditions”), it’s better to use broader principles to evaluate different choices. How much complexity is added by handling an additional input in a function implementation? Are there “reasonable” behaviours defined for a larger set of inputs than what you originally intended? The trade-offs are rarely clear cut.</p>
|
||||
<h2 id="thats-not-all">That’s not all!</h2>
|
||||
<p>It turns out that with either of the “precondition” or “reasonable default” strategies, our specification of <code>max_length</code> is still incomplete. Before moving onto the next section, take a moment to study these implementations and try to guess what the gap might be!</p>
|
||||
</section>
|
||||
<!--
|
||||
|
||||
## The assert statement
|
||||
|
||||
TODO The outline currently introduces assertions, but this is done already in the Testing Functions I reading.
|
||||
|
||||
An `assert` statement looks very similar to an if statement.
|
||||
But instead of branching into a specific path of the program, an `assert` statement instead raises an `AssertionError`.
|
||||
Typically, these errors will terminate the program.
|
||||
The syntax for an `assert` statement is:
|
||||
|
||||
```python
|
||||
assert <expression>[, <message>]
|
||||
```
|
||||
|
||||
The `assert` keyword must always be followed by a boolean expression.
|
||||
When the expression evaluates to `False`, an `AssertionError` is raised.
|
||||
We also have the option to include a message (i.e., a string) after the expression, separating the expression and the message with a comma.
|
||||
The message is used to give the user additional information caused by a failure.
|
||||
We can try this right inside the console:
|
||||
|
||||
```python
|
||||
>>> assert 0 == 0
|
||||
>>> assert 0 == 1
|
||||
Traceback (most recent call last):
|
||||
File "<input>", line 1, in <module>
|
||||
AssertionError
|
||||
>>> assert 0 == 1, 'Oops!'
|
||||
Traceback (most recent call last):
|
||||
File "<input>", line 1, in <module>
|
||||
AssertionError: Oops!
|
||||
```
|
||||
|
||||
Notice how when the expression evaluated to `True` (i.e., `0 == 0`), no error was raised and *nothing* was output on the console.
|
||||
This is the main advantage of assertions: the only time an assertion will cause an issue is when the assertion fails.
|
||||
Failed assertions also give us some information on where the failure occured.
|
||||
Because our current example is done directly in the console, this information is not very useful.
|
||||
But when our code is spread across multiple files with hundreds of lines of Python, this information becomes invaluable for tracking down issues.
|
||||
|
||||
|
||||
-->
|
||||
<!-- ## References
|
||||
|
||||
- [Appendix B.3 `python-ta`] -->
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,267 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.8 Richer Type Annotations</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.8 Richer Type Annotations</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>Recall our definition of <code>max_length</code> from the previous section:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a><span class="co"> - len(strings) > 0</span></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
|
||||
<p>Let us introduce another issue:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="op">>>></span> max_length({<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>})</span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a>Traceback (most recent call last):</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> File <span class="st">"<stdin>"</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op"><</span>module<span class="op">></span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> File <span class="st">"<stdin>"</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op"><</span>setcomp<span class="op">></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a><span class="pp">TypeError</span>: <span class="bu">object</span> of <span class="bu">type</span> <span class="st">'int'</span> has no <span class="bu">len</span>()</span></code></pre></div>
|
||||
<p>Once again, our specification of valid inputs has failed us. The parameter type for <code>max_length</code> is <code>set</code>, and in Python <code>set</code>s can contain the values of many different types. It is not until the function description that we see that the parameter is not just <em>any</em> set, but specifically a set of strings. We could make this requirement more explicit by introducing another precondition, but there is a better approach. In this section, we’ll learn how to use Python’s <code>typing</code> module to increase the specificity of our type annotations.</p>
|
||||
<h2 id="the-types-in-a-collection">The types in a collection</h2>
|
||||
<p>There are four collection types that have seen so far: <code>set</code>, <code>list</code>, <code>tuple</code>, and <code>dict</code>. These are analogous to the data types we’ve been using so far, with one key exception: we can specify the types of the values they can contained by writing them in square brackets. The table below shows these types and some examples; note that <code>T</code>, <code>T1</code>, etc. are variables that could be replaced with any data type.</p>
|
||||
<div class="fullwidth" style="font-size: 0.9em;">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 24%" />
|
||||
<col style="width: 75%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td><code>set[T]</code></td>
|
||||
<td>A set whose elements all have type <code>T</code></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td><code>list[T]</code></td>
|
||||
<td>A list whose elements all have type <code>T</code></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td><code>tuple[T1, T2, ...]</code></td>
|
||||
<td>A tuple whose first element has type <code>T1</code>, second element has type <code>T2</code>, etc.</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td><code>dict[T1, T2]</code></td>
|
||||
<td>A dictionary whose keys are of type <code>T1</code> and whose values are of type <code>T2</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>For example:</p>
|
||||
<ul>
|
||||
<li><code>{'hi', 'bye'}</code> has type <code>set[str]</code></li>
|
||||
<li><code>[1, 2, 3]</code> has type <code>list[int]</code></li>
|
||||
<li><code>('hello', True, 3.4)</code> has type <code>tuple[str, bool, float]</code></li>
|
||||
<li><code>{'a': 1, 'b': 2, 'c': 3}</code> has type <code>dict[str, int]</code></li>
|
||||
</ul>
|
||||
<p>Here is how we can improve the type contract for <code>max_length</code>:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> max_length(strings: <span class="bu">set</span>[<span class="bu">str</span>]) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">"""Return the maximum length of a string in the set of strings.</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a></span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a><span class="co"> - len(strings) > 0</span></span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a> <span class="cf">return</span> <span class="bu">max</span>({<span class="bu">len</span>(s) <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
|
||||
<h3 id="general-collections">General collections</h3>
|
||||
<p>Though indicating the type of the values inside a collection is useful, it is not always necessary. Sometimes we <em>want</em> to be flexible and say that a value must be a list, but we don’t care what’s in the list (could be a list of strings, a list of integers, or a list of strings mixed with integers). Or, we might want a list (or other collection) with elements of different types. In such cases, we will continue using the built-in types <code>set</code>, <code>list</code>, <code>tuple</code>, and <code>dict</code>, for these types annotations <em>without</em> additional information.</p>
|
||||
<h2 id="applying-what-weve-learned">Applying what we’ve learned</h2>
|
||||
<p>Let us revisit a function we designed when discussing if statements:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> get_status_v3(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">"""Return the flight status for the given scheduled and estimated departure times.</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a><span class="co"> The times are given as integers between 0 and 23 inclusive, representing</span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a><span class="co"> the hour of the day.</span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a><span class="co"> The status is 'On time', 'Delayed', or 'Cancelled'.</span></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a><span class="co"> """</span></span></code></pre></div>
|
||||
<p>How can we improve the specification of this function? Looking at the type annotations we see that, since none are collection types, we cannot make them any more specific than they already are. Next, looking at the docstring we see that there is the potential for some preconditions:<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> We kept the English description of what the times represent, but moved the Python-checkable part into formal preconditions.</span></p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> get_status_v3(scheduled: <span class="bu">int</span>, estimated: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">"""Return the flight status for the given scheduled and estimated departure times.</span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a><span class="co"> The times given represent the hour of the day.</span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a><span class="co"> - 0 <= scheduled <= 23</span></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a><span class="co"> - 0 <= estimated <= 23</span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a><span class="co"> """</span></span></code></pre></div>
|
||||
<p>Next let us revisit the <code>count_cancelled</code> function we designed:</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">def</span> count_cancelled(flights: <span class="bu">dict</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a> <span class="co">"""Return the number of cancelled flights for the given flight data.</span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a></span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a><span class="co"> flights is a dictionary where each key is a flight ID,</span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a><span class="co"> and whose corresponding value is a list of two numbers, where the first is</span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a><span class="co"> the scheduled departure time and the second is the estimated departure time.</span></span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a></span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a><span class="co"> >>> count_cancelled({'AC110': [10, 12], 'AC321': [12, 19], 'AC999': [1, 1]})</span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a><span class="co"> 1</span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a><span class="co"> """</span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11"></a> cancelled_flights <span class="op">=</span> {<span class="bu">id</span> <span class="cf">for</span> <span class="bu">id</span> <span class="kw">in</span> flights</span>
|
||||
<span id="cb6-12"><a href="#cb6-12"></a> <span class="cf">if</span> get_status2(flights[<span class="bu">id</span>][<span class="dv">0</span>], flights[<span class="bu">id</span>][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span>}</span>
|
||||
<span id="cb6-13"><a href="#cb6-13"></a> <span class="cf">return</span> <span class="bu">len</span>(cancelled_flights)</span></code></pre></div>
|
||||
<p>Here we can improve the type annotations. The first parameter is not just a <code>dict</code>, but a <code>dict[str, list[int]]</code>—that is, its keys are strings (the flight IDs), and the corresponding value is a list of integers. Does this type annotation mean that now the documentation describing the dictionary is irrelevant? No: while the type annotation gives some insight on the structure of the data, it does not provide domain-specific context, like the fact that the <code>str</code> keys represent flight IDs, or that the list values represent scheduled and estimated arrival departure times.</p>
|
||||
<p>There is one more precondition that we can formalize, though: the length of each list in our dictionary. <em>Every</em> list should have length two, which translates naturally into a use of Python’s <code>all</code> function:</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> count_cancelled(flights: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">"""Return the number of cancelled flights for the given flight data.</span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> flights is a dictionary where each key is a flight ID,</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> and whose corresponding value is a list of two numbers, where the first is</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a><span class="co"> the scheduled departure time and the second is the estimated departure time.</span></span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a></span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a><span class="co"> Precondition:</span></span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a><span class="co"> - all(len(flights[k]) == 2 for k in flights)</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a></span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a><span class="co"> >>> count_cancelled({'AC110': [10, 12], 'AC321': [12, 19], 'AC999': [1, 1]})</span></span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a><span class="co"> 1</span></span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a><span class="co"> """</span></span>
|
||||
<span id="cb7-14"><a href="#cb7-14"></a> cancelled_flights <span class="op">=</span> {<span class="bu">id</span> <span class="cf">for</span> <span class="bu">id</span> <span class="kw">in</span> flights</span>
|
||||
<span id="cb7-15"><a href="#cb7-15"></a> <span class="cf">if</span> get_status2(flights[<span class="bu">id</span>][<span class="dv">0</span>], flights[<span class="bu">id</span>][<span class="dv">1</span>]) <span class="op">==</span> <span class="st">'Cancelled'</span>}</span>
|
||||
<span id="cb7-16"><a href="#cb7-16"></a> <span class="cf">return</span> <span class="bu">len</span>(cancelled_flights)</span></code></pre></div>
|
||||
<h2 id="references">References</h2>
|
||||
<ul>
|
||||
<li><a href="../B-python-libraries/04-typing.html">B.4 <code>typing</code></a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,273 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.9 Working with Definitions</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.9 Working with Definitions</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>Throughout this course, we will study various mathematical objects that play key roles in computer science. As these objects become more complex, so too will our statements about them, to the point where if we try to write out everything using just basic set and arithmetic operations, our formulas won’t fit on a single line! To avoid this problem, we create <em>definitions</em>, which we can use to express a long idea using a single term.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote">This is analogous to using local variables or helper functions in programming to express <em>part</em> of an overall value or computation.</span></p>
|
||||
<p>In this section, we’ll look at one extended example of defining our own predicates mathematically and in Python, and using them in our statements. Let us take some familiar terminology and make it precise using the languages of predicate logic and Python.</p>
|
||||
<div class="definition" data-terms="divisibility">
|
||||
<p>Let <span class="math inline">\(n, d \in \Z\)</span>.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote">You may be used to defining divisibility for just the natural numbers, but it will be helpful to allow for negative numbers in our work.</span> We say that <strong><span class="math inline">\(d\)</span> divides <span class="math inline">\(n\)</span></strong>, or <strong><span class="math inline">\(n\)</span> is divisible by <span class="math inline">\(d\)</span></strong>, when there exists a <span class="math inline">\(k \in \Z\)</span> such that <span class="math inline">\(n = dk\)</span>. In this case, we use the notation <span class="math inline">\(d \DIV n\)</span> to represent “<span class="math inline">\(d\)</span> divides <span class="math inline">\(n\)</span>.”</p>
|
||||
<p>Note that just like the equals sign <span class="math inline">\(=\)</span> is a binary predicate, so too is <span class="math inline">\(\DIV\)</span>. For example, the statement <span class="math inline">\(3 \DIV 6\)</span> is True, while the statement <span class="math inline">\(4 \DIV 10\)</span> is False.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote">Students often confuse the divisibility predicate with the horizontal fraction bar. The former is a <em>predicate</em> that returns a boolean; the latter is a function that returns a number. So <span class="math inline">\(4 \DIV 10\)</span> is <span class="math inline">\(False\)</span>, while <span class="math inline">\(\frac{10}{4}\)</span> is <span class="math inline">\(2.5\)</span>.</span></p>
|
||||
<p>This definition also permits <span class="math inline">\(d = 0\)</span>, which may be a bit surprising! According to this definition, <span class="math inline">\(0 \mid 0\)</span>, and for any non-zero <span class="math inline">\(n \in \Z\)</span>, <span class="math inline">\(0 \nmid n\)</span>.<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote">Exercise: why are these two statements true?</span> In other words, when <span class="math inline">\(d = 0\)</span>, <span class="math inline">\(d \mid n\)</span> <em>if and only if</em> <span class="math inline">\(n = 0\)</span>.</p>
|
||||
</div>
|
||||
<div class="example">
|
||||
<p>Let’s express the statement “For every integer <span class="math inline">\(x\)</span>, if <span class="math inline">\(x\)</span> divides 10, then it also divides 100” in two ways: with the divisibility predicate <span class="math inline">\(d \DIV n\)</span>, and without it.</p>
|
||||
<ul>
|
||||
<li><p><strong>With the predicate</strong>: this is a universal quantification over all possible integers, and contains a logical implication. So we can write <span class="math display">\[\forall x \in \Z,~ x \DIV 10 \IMP x \DIV 100.\]</span></p></li>
|
||||
<li><p><strong>Without the predicate</strong>: the same structure is there, except we <em>unpack the definition</em> of divisibility, replacing every instance of <span class="math inline">\(d \DIV n\)</span> with <span class="math inline">\(\exists k \in \Z,~ n = dk\)</span>. <span class="math display">\[\forall x \in \Z,~ \big(\exists k \in \Z,~ 10 = kx\big) \IMP \big(\exists k \in \Z,~ 100 = kx\big).\]</span></p>
|
||||
<p>Note that each subformula in the parentheses has its own <span class="math inline">\(k\)</span> variable, whose scope is limited by the parentheses.<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote">That is, the <span class="math inline">\(k\)</span> in the hypothesis of the implication is different from the <span class="math inline">\(k\)</span> in the conclusion: they can take on different values, though they can also take on the same value.</span> However, even though this technically correct, it’s often confusing for beginners. So instead, we’ll tweak the variable names to emphasize their distinctness: <span class="math display">\[\forall x \in \Z,~ \big(\exists k_1 \in \Z,~ 10 = k_1x\big) \IMP \big(\exists k_2 \in \Z,~ 100 = k_2x\big).\]</span></p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>As you can see, using this new predicate makes our formula quite a bit more concise! But the usefulness of our definitions doesn’t stop here: we can, of course, use our terms and predicates in further definitions.</p>
|
||||
<div class="definition" data-terms="prime">
|
||||
<p>Let <span class="math inline">\(p \in \Z\)</span>. We say <span class="math inline">\(p\)</span> is <strong>prime</strong> when it is greater than <span class="math inline">\(1\)</span> and the only natural numbers that divide it are <span class="math inline">\(1\)</span> and itself.</p>
|
||||
</div>
|
||||
<div class="example">
|
||||
<p>Let’s define a predicate <span class="math inline">\(IsPrime(p)\)</span> to express the statement that “<span class="math inline">\(p\)</span> is a prime number,” with and without using the divisibility predicate.</p>
|
||||
<p>The first part of the definition, “greater than <span class="math inline">\(1\)</span>,” is straightforward. The second part is a bit trickier, but a good insight is that we can enforce constraints on values through implication: <em>if a number <span class="math inline">\(d\)</span> divides <span class="math inline">\(p\)</span>, then <span class="math inline">\(d = 1\)</span> or <span class="math inline">\(d = p\)</span></em>. We can put these two ideas together to create a formula: <span class="math display">\[IsPrime(p): p > 1 \AND \big( \forall d \in \N,~ d \DIV p \IMP d = 1 \OR d = p \big), \qquad \text{where $p \in \Z$}.\]</span></p>
|
||||
<p>To express this idea without using divisibility predicate, we substitute in the definition of divisibility. The underline shows the changed part. <span class="math display">\[IsPrime(p): p > 1 \AND \big( \forall d \in \N,~ \underline{\left(\exists k \in \Z,~ p = kd\right)} \IMP d = 1 \OR d = p \big), \quad \text{where $p \in \Z$}.\]</span></p>
|
||||
</div>
|
||||
<h2 id="expressing-definitions-in-programs">Expressing definitions in programs</h2>
|
||||
<p>As we just saw, in mathematics we can often express definitions as predicates, where an element of the domain (e.g., an integer) satisfies the predicate if it fits the definition. Because predicates are just functions, we can express these in programs as well. For example, let’s consider the divisibility predicate <span class="math inline">\(\mid\)</span>, where <span class="math inline">\(d \mid n\)</span> means <span class="math inline">\(\exists k \in \Z,~ n = kd\)</span> (for <span class="math inline">\(d, n \in \Z\)</span>). Here is the start of a function design in Python:</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> divides(d: <span class="bu">int</span>, n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">"""Return whether d divides n."""</span></span></code></pre></div>
|
||||
<p>While we can use the modulo operator <code>%</code> to implement this function (more on this later), we’ll stick to remaining faithful to the mathematical definition as much as possible. Unfortunately, there is one challenge with translating the mathematical definition of divisibility precisely into a Python function. In mathematics we have no trouble at all representing an infinite set of numbers with the symbol <span class="math inline">\(\Z\)</span>; but in a computer program, we cannot represent infinite sets in the same way. Instead, we’ll use a <em>property</em> of divisibility to restrict the set of numbers to quantify over: when <span class="math inline">\(n \neq 0\)</span>, every number that divides <span class="math inline">\(n\)</span> must lie in the range <span class="math inline">\(\{-|n|, -|n| + 1, \dots, |n| - 1, |n|\}\)</span>.<label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote"> We’ll actually <em>prove</em> this property later on!</span></p>
|
||||
<p>But the next question is, how do we represent the set <span class="math inline">\(\{-|n|, -|n| + 1, \dots, |n| - 1, |n|\}\)</span> in Python, when the <span class="math inline">\(n\)</span> is given as a parameter? We can use the <code>range</code> data type:<label for="sn-6" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-6" class="margin-toggle"/><span class="sidenote"> Remember the asymmetry here: the <code>start</code> argument is inclusive, but the <code>end</code> argument is exclusive.</span></p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a>possible_divisors <span class="op">=</span> <span class="bu">range</span>(<span class="op">-</span><span class="bu">abs</span>(n), <span class="bu">abs</span>(n) <span class="op">+</span> <span class="dv">1</span>)</span></code></pre></div>
|
||||
<p>And then we can replace <span class="math inline">\(\Z\)</span> by this variable in the definition of divisibility to obtain <span class="math inline">\(\exists k \in possible\_divisors,~ n = kd\)</span>. We can now translate this directly into Python code using what we learned earlier this chapter:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> divides(d: <span class="bu">int</span>, n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">"""Return whether d divides n."""</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> possible_divisors <span class="op">=</span> <span class="bu">range</span>(<span class="op">-</span> <span class="bu">abs</span>(n), <span class="bu">abs</span>(n) <span class="op">+</span> <span class="dv">1</span>)</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a> <span class="cf">return</span> <span class="bu">any</span>({n <span class="op">==</span> k <span class="op">*</span> d <span class="cf">for</span> k <span class="kw">in</span> possible_divisors})</span></code></pre></div>
|
||||
<!-- TODO: Could ask the students, does this definition work when n = 0? When d = 0? -->
|
||||
<p>Now let’s turn our attention to the definition of <span class="math inline">\(\mathit{IsPrime}\)</span>:</p>
|
||||
<p><span class="math display">\[\mathit{IsPrime}(p): p > 1 \AND \big( \forall d \in \N,~ d \DIV p \IMP d = 1 \OR d = p \big), \qquad \text{where $p \in \Z$}.\]</span></p>
|
||||
<p>Here’s a start for translating this definition into a Python function:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> is_prime(p: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">"""Return whether p is prime."""</span></span></code></pre></div>
|
||||
<p>Once again, we have a problem of an infinite set: <span class="math inline">\(\forall d \in \N\)</span>. We can use the same property of divisibility as above and note that the possible natural numbers that are divisors of <code>p</code> are in the set <span class="math inline">\(\{1, 2, \dots, p\}\)</span>.<label for="sn-7" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-7" class="margin-toggle"/><span class="sidenote"> This is simpler than the version above because <span class="math inline">\(p \geq 1\)</span>.</span> The quantified statement is a bit harder to translate because it contains an implication, so here we recall what we discussed in <a href="03-filtering-collections.html">3.3 Filtering Collections</a> to use the <code>if</code> keyword in a comprehension to model implications. Here is our complete implementation of <code>is_prime</code>:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> is_prime(p: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">"""Return whether p is prime."""</span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> possible_divisors <span class="op">=</span> <span class="bu">range</span>(<span class="dv">1</span>, p <span class="op">+</span> <span class="dv">1</span>)</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> <span class="cf">return</span> (</span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> p <span class="op">></span> <span class="dv">1</span> <span class="kw">and</span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> <span class="bu">all</span>({d <span class="op">==</span> <span class="dv">1</span> <span class="kw">or</span> d <span class="op">==</span> p <span class="cf">for</span> d <span class="kw">in</span> possible_divisors <span class="cf">if</span> divides(d, p)})</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> )</span></code></pre></div>
|
||||
<p>Notice that just like the mathematical definition, in Python our implementation of <code>is_prime</code> uses the <code>divides</code> function. This is a great example of how useful it can be to divide our work into functions that build on each other, rather than writing all of our code in a single function. As we learn about more complex domains in this course, we’ll see this pattern repeat itself: definitions will build on top of one another, and you should expect that your functions will build on one another as well.</p>
|
||||
<h2 id="divisibility-and-the-remainder-operation">Divisibility and the remainder operation</h2>
|
||||
<p>You might have noticed that our definition of <code>divides</code>, though faithful to the mathematical definition, is not the same as how we’ve previously determined whether a number is divisible by 2 (i.e., is even).</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">def</span> is_even(n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a> <span class="co">"""Return whether n is even."""</span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> <span class="cf">return</span> n <span class="op">%</span> <span class="dv">2</span> <span class="op">==</span> <span class="dv">0</span></span></code></pre></div>
|
||||
<p>In this case, we check whether <code>n</code> is divisible by 2 by checking whether the remainder when <code>n</code> is divided by 2 is 0 or not. It turns out that for <em>non-zero</em> <span class="math inline">\(d \in Z\)</span>, checking remainders is equivalent to the original definition of divisibility:</p>
|
||||
<p><span class="math display">\[\forall n, d \in \Z,~ d \neq 0 \Rightarrow (d \mid n \Leftrightarrow n~\%~d = 0).\]</span></p>
|
||||
<p>Note that when <span class="math inline">\(d = 0\)</span>, the remainder <span class="math inline">\(n~\%~d\)</span> is undefined, and so we really do need the <span class="math inline">\(d \neq 0\)</span> condition in the above statement.</p>
|
||||
<p>We can use this observation to write an alternate implementation of the <code>divides</code> function:</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> divides2(d: <span class="bu">int</span>, n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">"""Return whether d divides n."""</span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a> <span class="cf">if</span> d <span class="op">==</span> <span class="dv">0</span>:</span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a> <span class="co"># This is the original definition.</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> possible_divisors <span class="op">=</span> <span class="bu">range</span>(<span class="op">-</span><span class="bu">abs</span>(n), <span class="bu">abs</span>(n) <span class="op">+</span> <span class="dv">1</span>)</span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a> <span class="cf">return</span> <span class="bu">any</span>({n <span class="op">==</span> k <span class="op">*</span> d <span class="cf">for</span> k <span class="kw">in</span> possible_divisors})</span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a> <span class="co"># This is a new but equivalent check.</span></span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a> <span class="cf">return</span> n <span class="op">%</span> d <span class="op">==</span> <span class="dv">0</span></span></code></pre></div>
|
||||
<p>You might also notice that the <code>d == 0</code> case is quite special: according to our definition of divisibility, when <code>d == 0</code> we know that <code>d</code> divides <code>n</code> if and only if <code>n == 0</code>:</p>
|
||||
<p><span class="math display">\[\forall n, d \in \Z,~ d = 0 \Rightarrow (d \mid n \Leftrightarrow n = 0)\]</span></p>
|
||||
<p>We can use this to greatly simplify the if branch in our <code>divides2</code> function:</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">def</span> divides3(d: <span class="bu">int</span>, n: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a> <span class="co">"""Return whether d divides n."""</span></span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a> <span class="cf">if</span> d <span class="op">==</span> <span class="dv">0</span>:</span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a> <span class="co"># This is another new, equivalent check.</span></span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a> <span class="cf">return</span> n <span class="op">==</span> <span class="dv">0</span></span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a> <span class="cf">else</span>:</span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a> <span class="co"># This is a new but equivalent check.</span></span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a> <span class="cf">return</span> n <span class="op">%</span> d <span class="op">==</span> <span class="dv">0</span></span></code></pre></div>
|
||||
<p>Our implementation in <code>divides3</code> meets the same function specification as the original <code>divides</code>, but has a much simpler implementation! It is also much more <em>efficient</em> than the original <code>divides</code>, meaning it performs fewer calculations (or computational “steps”) and takes less time to compute its result. Intuitively, this is because the original <code>divides</code> function used the value <code>range(-abs(n), abs(n) + 1)</code> in a comprehension, and so the number of expressions evaluated gets larger as <code>n</code> grows. This is not the case for <code>divides3</code>, which does not use a single <code>range</code> or comprehension in its body!</p>
|
||||
<p>What this also means is that we can speed up our implementation of <code>is_prime</code> simply by calling <code>divides3</code> instead of <code>divides</code>:</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">def</span> is_prime(p: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a> <span class="co">"""Return whether p is prime."""</span></span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a> possible_divisors <span class="op">=</span> <span class="bu">range</span>(<span class="dv">1</span>, p <span class="op">+</span> <span class="dv">1</span>)</span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a> <span class="cf">return</span> (</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> p <span class="op">></span> <span class="dv">1</span> <span class="kw">and</span></span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a> <span class="bu">all</span>({d <span class="op">==</span> <span class="dv">1</span> <span class="kw">or</span> d <span class="op">==</span> p <span class="cf">for</span> d <span class="kw">in</span> possible_divisors <span class="cf">if</span> divides3(d, p)}) <span class="co"># <-- Note the "divides3"</span></span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a> )</span></code></pre></div>
|
||||
<p>This is a very powerful idea: we started out with one implementation of the divisibility predicate (<code>divides</code>), and then through some mathematical reasoning wrote a second implementation (<code>divides3</code>) that was logically equivalent to the first, but simpler and faster. But because <code>divides</code> and <code>divides3</code> were logically equivalent, we could safe replace <code>divides</code> with <code>divides3</code> in the implementation of <code>is_prime</code> to make it run faster, without worrying about introducing new errors!</p>
|
||||
<p>The idea of <em>swapping out implementations</em> will come up again and again in this course. As the functions and programs you write grows larger, efficiency will be an important consideration for your code, and so it will be common to start with one function implementation and eventually replace it with another. We’re only touching on these idea here with a relatively simple example, but we will talk formally about program efficiency in a later chapter.</p>
|
||||
<!-- ### A note about efficiency
|
||||
|
||||
The combination of our `is_prime` and `divides` code is actually extremely inefficient.
|
||||
To understand why, let us use the example function call `is_prime(5)`.
|
||||
How many `possible_divisors` will we have?
|
||||
|
||||
```python
|
||||
>>> p = 5
|
||||
>>> possible_divisors = range(1, p + 1)
|
||||
>>> list(possible_divisors)
|
||||
[1, 2, 3, 4, 5]
|
||||
```
|
||||
|
||||
Now let's consider the set comprehension in `is_prime`.
|
||||
The filtering condition, `if divides(d, p)`, will check every integer in `possible_divisors`.
|
||||
This means that our `divides` function will be called five times (`len(possible_divisors)`).
|
||||
But `divides` has its own set of `possible_divisors`, too!^[
|
||||
Remember: `range(- abs(n), abs(n) + 1)`.
|
||||
]
|
||||
Let's track this with a table:
|
||||
|
||||
`is_prime` calls to `divides` `possible_divisors` in `divides`
|
||||
----------------------------- --------------------------------
|
||||
`divides(1, 5)` `range(-abs(5), abs(5) + 1)`
|
||||
`divides(2, 5)` `range(-abs(5), abs(5) + 1)`
|
||||
`divides(3, 5)` `range(-abs(5), abs(5) + 1)`
|
||||
`divides(4, 5)` `range(-abs(5), abs(5) + 1)`
|
||||
`divides(5, 5)` `range(-abs(5), abs(5) + 1)`
|
||||
|
||||
There are two take-aways from this table.
|
||||
First, the number of rows in the table is equal to the integer (i.e., $p$) we pass to `is_prime` (here, we showed `5`, but imagine if we checked `143512`).
|
||||
Second, the range of `possible_divisors` in `divides` is roughly twice the integer we past to `is_prime`.
|
||||
Together, this means that Python is performing around $p \times 2 \times p$ calculations for any prime number we want to check.
|
||||
As a comparison, how many calculations would you need to do without Python to see if $5$ is prime?
|
||||
Is it less than $5 \times 2 \times 5 = 50$?
|
||||
Efficiency is an important part of computer science that we will talk about formally in a later chapter. -->
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,395 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.10 Testing Functions II: hypothesis</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.10 Testing Functions II: <code>hypothesis</code></h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>When we introduced if statements in <a href="04-if-statements.html">Section 3.4</a>, we discussed how unit tests could be used to perform <em>white box testing</em>, where the goal is to “cover” all possible execution paths with unit tests. Unit tests really excel in this scenario because we can determine what the inputs of a function should be to reach a particular branch.</p>
|
||||
<p>But choosing unit test inputs also imposes challenges on the programmer writing those tests. How do we know we have “enough” inputs? What properties of the inputs should we consider? For example, if our function takes a <code>list[int]</code>, how long should our input lists be, should they contain duplicates, and what should the values inside the list be? For each choice of answers to these questions, we then need to choose a specific input and calculate the expected output to write a unit test.</p>
|
||||
<p>In this section, we introduce a different form of testing called <em>property-based testing</em>, using the Python module <code>hypothesis</code>. The main advantage of property-based testing with <code>hypothesis</code> is that we can write one test case that calls the function being tested <em>multiple inputs</em> that the <code>hypothesis</code> library chooses for us automatically. Property-based tests are not intended to replace unit tests—both have their role in testing and both are important.</p>
|
||||
<h2 id="property-based-testing">Property-based testing</h2>
|
||||
<p>The kinds of tests we’ve discussed so far involve defining <em>input-output pairs</em>: for each test, we write a specific input to the function we’re testing, and then use <code>assert</code> statements to verify the correctness of the corresponding output. These tests have the advantage that writing any one individual test is usually straightforward, but the disadvantage that choosing and implementing test cases can be challenging and time-consuming.</p>
|
||||
<p>There is another way of constructing tests that we will explore here: <em>property-based testing</em>, in which a single test typically consists of a large set of possible inputs that is generated in a programmatic way. Such tests have the advantage that it is usually straightforward to cover a broad range of inputs in a short amount of code; but it isn’t always easy to specify exactly what the corresponding outputs should be. If we were to write code to compute the correct answer, how would we know that <em>that</em> code is correct?</p>
|
||||
<p>So instead, property-based tests use <code>assert</code> statements to check for <em>properties</em> that the function being tested should satisfy. In the simplest case, these are properties that every output of the function should satisfy, regardless of what the input was. For example:</p>
|
||||
<ul>
|
||||
<li>The <em>type</em> of the output: “the function <code>str</code> should always return a string.”</li>
|
||||
<li><em>Allowed values</em> of the output: “the function <code>len</code> should always return an integer that is greater than or equal to zero.”</li>
|
||||
<li><em>Relationships</em> between the input and output: “the function <code>max(x, y)</code> should return something that is greater than or equal to both <code>x</code> and <code>y</code>.”</li>
|
||||
<li><em>Relationships</em> between two (or more) input-output pairs: "for any two lists of numbers <code>nums1</code> and <code>nums2</code>, we know that <code>sum(nums1 + nums2) == sum(nums1) + sum(nums2)</code>.</li>
|
||||
</ul>
|
||||
<p>These properties may seem a little strange, because they do not capture precisely what each function does; for example, <code>str</code> should not just return any string, but a string that represents its input. This is the trade-off that comes with property-based testing: in exchange for being able to run our code on a much larger range of inputs, we write tests which are imprecise characterizations of the function’s inputs. The challenge, then, with property-based testing is to come up with good properties that narrow down as much as possible the behaviour of the function being tested.</p>
|
||||
<h2 id="using-hypothesis">Using <code>hypothesis</code></h2>
|
||||
<p>As a first example, let’s consider our familiar <code>is_even</code> function, which we define in a file called <code>my_functions.py</code>:<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote">You can follow along in this section by creating your own files!</span></p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="co"># Suppose we've saved this in my_functions.py</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a><span class="kw">def</span> is_even(value: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a> <span class="co">"""Return whether value is divisible by 2.</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a><span class="co"> >>> is_even(2)</span></span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a><span class="co"> True</span></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a><span class="co"> >>> is_even(17)</span></span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a><span class="co"> False</span></span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a><span class="co"> """</span></span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a> <span class="cf">return</span> value <span class="op">%</span> <span class="dv">2</span> <span class="op">==</span> <span class="dv">0</span></span></code></pre></div>
|
||||
<p>Rather than choosing specific inputs to test <code>is_even</code> on, we’re going to test the following two <em>properties</em>:</p>
|
||||
<ul>
|
||||
<li><code>is_even</code> always returns <code>True</code> when given an <code>int</code> of the form <code>2 * x</code> (where <code>x</code> is an <code>int</code>)</li>
|
||||
<li><code>is_even</code> always returns <code>False</code> when given an <code>int</code> of the form <code>2 * x + 1</code> (where <code>x</code> is an <code>int</code>)</li>
|
||||
</ul>
|
||||
<p>One of the benefits of our previous study of predicate logic is that we can express both of these properties clearly and unambiguously using symbolic notation:</p>
|
||||
<p><span class="math display">\[\begin{align*}
|
||||
\forall x \in \Z,~ \text{is_even}(2x) \\
|
||||
\forall x \in \Z,~ \lnot \text{is_even}(2x + 1)
|
||||
\end{align*}\]</span></p>
|
||||
<p>Now let’s see how to express these properties as test cases using <code>hypothesis</code>. First, we create a new file called <code>test_my_functions.py</code>, and include the following “test” function:<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Make sure that <code>my_functions.py</code> and <code>test_my_functions.py</code> are in the same directory.</span></p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="co"># In file test_my_functions.py</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="im">from</span> my_functions <span class="im">import</span> is_even</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a><span class="kw">def</span> test_is_even_2x(x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> <span class="co">"""Test that is_even returns True when given a number of the form 2*x."""</span></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> <span class="cf">assert</span> is_even(<span class="dv">2</span> <span class="op">*</span> x)</span></code></pre></div>
|
||||
<p>Note that unlike previous tests we’ve written, we have not chosen a specific input value for <code>is_even</code>! Instead, our test function <code>test_is_even_2x</code> takes an an integer for <code>x</code>, and calls <code>is_even</code> on <code>2 * x</code>. This is a more general form of test because now <code>x</code> could be any integer.</p>
|
||||
<p>So now the question is, how do we actually call <code>test_is_even_2x</code> on many different integer values?<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> You could run this file in the Python console and call it manually on different arguments, but there must be a better way!</span> This is where <code>hypothesis</code> comes in. In order to generate a range of inputs, the <code>hypothesis</code> module offers a set of <em>strategies</em> that we can use. These strategies are able to generate several values of a specific type of input. For example, to generate <code>int</code> data types, we can use the <code>integers</code> strategy. To start, we add these two lines to the top of our test file:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="co"># In file test_my_functions.py</span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="im">from</span> hypothesis <span class="im">import</span> given <span class="co"># NEW</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a><span class="im">from</span> hypothesis.strategies <span class="im">import</span> integers <span class="co"># NEW</span></span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a><span class="im">from</span> my_functions <span class="im">import</span> is_even</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a></span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a></span>
|
||||
<span id="cb3-8"><a href="#cb3-8"></a><span class="kw">def</span> test_is_even_2x(x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb3-9"><a href="#cb3-9"></a> <span class="co">"""Test that is_even returns True when given a number of the form 2*x."""</span></span>
|
||||
<span id="cb3-10"><a href="#cb3-10"></a> <span class="cf">assert</span> is_even(<span class="dv">2</span> <span class="op">*</span> x)</span></code></pre></div>
|
||||
<p>Just importing <code>given</code> and <code>integers</code> isn’t enough, of course. We need to somehow “attach” them to our test function so that <code>hypothesis</code> knows to generate integer inputs for the test. To do so, we use a new piece of Python syntax called a <strong>decorator</strong>, which is specified by using the <code>@</code> symbol with an expression in the line immediately before a function definition. Here is the use of a decorator in action:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="co"># In file test_my_functions.py</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><span class="im">from</span> hypothesis <span class="im">import</span> given</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a><span class="im">from</span> hypothesis.strategies <span class="im">import</span> integers</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a><span class="im">from</span> my_functions <span class="im">import</span> is_even</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a><span class="at">@given</span>(x<span class="op">=</span>integers()) <span class="co"># NEW</span></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a><span class="kw">def</span> test_is_even_2x(x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> <span class="co">"""Test that is_even returns True when given a number of the form 2*x."""</span></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> <span class="cf">assert</span> is_even(<span class="dv">2</span> <span class="op">*</span> x)</span></code></pre></div>
|
||||
<p>The line <code>@given(x=integers())</code> is a bit tricky, so let’s unpack it. First, <code>integers</code> is a <code>hypothesis</code> function that returns a special data type called a <strong>strategy</strong>, which is what <code>hypothesis</code> uses to generate a range of possible inputs. In this case, calling <code>integers()</code> returns a strategy that simply generates <code>int</code>s.</p>
|
||||
<p>Second, <code>given</code> is a <code>hypothesis</code> function that takes in arguments in the form <code><param>=<strategy></code>, which acts as a mapping for the test parameter name to a strategy that <code>hypothesis</code> should use for generating arguments for that parameter.</p>
|
||||
<p>We say that the line <code>@given(x=integers())</code> <em>decorates</em> the test function, so that when we run the test function, <code>hypothesis</code> will call the test several times, using <code>int</code> values for <code>x</code> as specified by the strategy <code>integers()</code>. Essentially, <code>@given</code> helps automate the process of “run the test on different <code>int</code> values” for us!</p>
|
||||
<p>And finally, To actually run the test, we use <code>pytest</code>, just like before:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="co"># In file test_my_functions.py</span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a><span class="im">from</span> hypothesis <span class="im">import</span> given</span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a><span class="im">from</span> hypothesis.strategies <span class="im">import</span> integers</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a><span class="im">from</span> my_functions <span class="im">import</span> is_even</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a><span class="at">@given</span>(x<span class="op">=</span>integers())</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a><span class="kw">def</span> test_is_even_2x(x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> <span class="co">"""Test that is_even returns True when given a number of the form 2*x."""</span></span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> <span class="cf">assert</span> is_even(<span class="dv">2</span> <span class="op">*</span> x)</span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a></span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a></span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">'__main__'</span>:</span>
|
||||
<span id="cb5-15"><a href="#cb5-15"></a> <span class="im">import</span> pytest</span>
|
||||
<span id="cb5-16"><a href="#cb5-16"></a> pytest.main([<span class="st">'test_my_functions.py'</span>, <span class="st">'-v'</span>])</span></code></pre></div>
|
||||
<h3 id="testing-odd-values">Testing odd values</h3>
|
||||
<p>Just like with unit tests, we can write multiple property-based tests in the same file and have <code>pytest</code> run each of them. Here is our final version of <code>test_my_functions.py</code> for this example, which adds a second test for numbers of the form <span class="math inline">\(2x + 1\)</span>.</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="co"># In file test_my_functions.py</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a><span class="im">from</span> hypothesis <span class="im">import</span> given</span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a><span class="im">from</span> hypothesis.strategies <span class="im">import</span> integers</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a><span class="im">from</span> my_functions <span class="im">import</span> is_even</span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a></span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a></span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a><span class="at">@given</span>(x<span class="op">=</span>integers())</span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a><span class="kw">def</span> test_is_even_2x(x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> <span class="co">"""Test that is_even returns True when given a number of the form 2*x."""</span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11"></a> <span class="cf">assert</span> is_even(<span class="dv">2</span> <span class="op">*</span> x)</span>
|
||||
<span id="cb6-12"><a href="#cb6-12"></a></span>
|
||||
<span id="cb6-13"><a href="#cb6-13"></a></span>
|
||||
<span id="cb6-14"><a href="#cb6-14"></a><span class="at">@given</span>(x<span class="op">=</span>integers())</span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a><span class="kw">def</span> test_is_even_2x_plus_1(x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a> <span class="co">"""Test that is_even returns False when given a number of the form 2*x + 1."""</span></span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a> <span class="cf">assert</span> <span class="kw">not</span> is_even(<span class="dv">2</span> <span class="op">*</span> x <span class="op">+</span> <span class="dv">1</span>)</span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a></span>
|
||||
<span id="cb6-19"><a href="#cb6-19"></a></span>
|
||||
<span id="cb6-20"><a href="#cb6-20"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">'__main__'</span>:</span>
|
||||
<span id="cb6-21"><a href="#cb6-21"></a> <span class="im">import</span> pytest</span>
|
||||
<span id="cb6-22"><a href="#cb6-22"></a> pytest.main([<span class="st">'test_my_functions.py'</span>, <span class="st">'-v'</span>])</span></code></pre></div>
|
||||
<!--
|
||||
|
||||
We would also expect `is_even` to evaluate to `False` whenever an odd number is passed as an argument.
|
||||
But how can we turn the number generated by the `integers` strategy into an odd number?
|
||||
We could use a similar mathematical trick (e.g., multiply by two and add one), but instead we will use this opportunity to introduce a new hypothesis feature: `hypothesis.assume`.
|
||||
With `assume`, we can communicate to `hypothesis` that the value generated by a strategy should not be tested:
|
||||
|
||||
|
||||
```python
|
||||
# Note the change: we also import assume
|
||||
from hypothesis import given, assume
|
||||
from hypothesis.strategies import integers
|
||||
|
||||
from my_functions import is_even
|
||||
|
||||
@given(integers())
|
||||
def test_is_even_on_odd_number(value: int):
|
||||
assume(value % 2 == 1)
|
||||
assert is_even(value) == False
|
||||
```
|
||||
|
||||
If we did not include `assume(value % 2 == 1)`, then the `integers` strategy may produce an even number, causing our assertion to fail even for a correct implementation of `is_even`.
|
||||
However, by including the `assume` statement, we ensure that `hypothesis` skips the assertion when `value` is even (in other words, we assume that `value` is odd). -->
|
||||
<h2 id="using-hypothesis-with-collections">Using <code>hypothesis</code> with collections</h2>
|
||||
<p>Now let’s consider a more complicated example, this time involving lists of integers. Let’s add the following function to <code>my_functions.py</code>:</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="co"># In my_functions.py</span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="kw">def</span> num_evens(nums: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> <span class="co">"""Return the number of even elements in nums."""</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a> <span class="cf">return</span> <span class="bu">len</span>([n <span class="cf">for</span> n <span class="kw">in</span> nums <span class="cf">if</span> is_even(n)])</span></code></pre></div>
|
||||
<p>Let’s look at one example of a property-based test for <code>num_evens</code>. For practice, we’ll express this property in predicate logic first. Let <span class="math inline">\(\mathcal{L}_{int}\)</span> be the set of lists of integers. The property we’ll express is:</p>
|
||||
<p><span class="math display">\[
|
||||
\forall \text{nums} \in \mathcal{L}_{\text{int}},~ \forall x \in \Z,~ \text{num_evens}(\text{nums} + [2x]) = \text{num_evens}(\text{nums}) + 1
|
||||
\]</span></p>
|
||||
<p>Translated into English: for any list of integers <span class="math inline">\(nums\)</span> and any integer <span class="math inline">\(x\)</span>, the number of even elements of <code>nums + [2 * x]</code> is one more than the number of even elements of <code>nums</code>.</p>
|
||||
<p>We can start using the same idea as our <code>is_even</code> example, by writing the test function in <code>test_my_functions.py</code>.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="co"># In test_my_functions.py</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a><span class="kw">def</span> test_num_evens_one_more_even(nums: <span class="bu">list</span>[<span class="bu">int</span>], x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a> <span class="co">"""Test num_evens when you add one more even element."""</span></span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a> <span class="cf">assert</span> num_evens(nums <span class="op">+</span> [<span class="dv">2</span> <span class="op">*</span> x]) <span class="op">==</span> num_evens(nums) <span class="op">+</span> <span class="dv">1</span></span></code></pre></div>
|
||||
<p>Now we need to use <code>@given</code> again to tell <code>hypothesis</code> to generate inputs for this test function. Because this function takes two arguments, we know that we’ll need a decorator expression of the form</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="at">@given</span>(nums<span class="op">=</span>..., x<span class="op">=</span>...)</span></code></pre></div>
|
||||
<p>We can reuse the same <code>integers()</code> strategy for <code>x</code>, but what about <code>nums</code>? Not surprisingly, we can import the <code>lists</code> function from <code>hypothesis.strategies</code> to create strategies for generating lists! The <code>lists</code> function takes in a single argument, which is a strategy for generating the elements of the list. In our example, we can use <code>lists(integers())</code> to return a strategy for generating lists of integers.</p>
|
||||
<p>Here is our full test file (with the <code>is_even</code> tests omitted):</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="co"># In file test_my_functions.py</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a><span class="im">from</span> hypothesis <span class="im">import</span> given</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a><span class="im">from</span> hypothesis.strategies <span class="im">import</span> integers, lists <span class="co"># NEW lists import</span></span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a><span class="im">from</span> my_functions <span class="im">import</span> is_even, num_evens</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a></span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a></span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a><span class="at">@given</span>(nums<span class="op">=</span>lists(integers()), x<span class="op">=</span>integers()) <span class="co"># NEW given call</span></span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a><span class="kw">def</span> test_num_evens_one_more_even(nums: <span class="bu">list</span>[<span class="bu">int</span>], x: <span class="bu">int</span>) <span class="op">-></span> <span class="va">None</span>:</span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a> <span class="co">"""Test num_evens when you add one more even element."""</span></span>
|
||||
<span id="cb10-11"><a href="#cb10-11"></a> <span class="cf">assert</span> num_evens(nums <span class="op">+</span> [<span class="dv">2</span> <span class="op">*</span> x]) <span class="op">==</span> num_evens(nums) <span class="op">+</span> <span class="dv">1</span></span>
|
||||
<span id="cb10-12"><a href="#cb10-12"></a></span>
|
||||
<span id="cb10-13"><a href="#cb10-13"></a></span>
|
||||
<span id="cb10-14"><a href="#cb10-14"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">'__main__'</span>:</span>
|
||||
<span id="cb10-15"><a href="#cb10-15"></a> <span class="im">import</span> pytest</span>
|
||||
<span id="cb10-16"><a href="#cb10-16"></a> pytest.main([<span class="st">'test_my_functions.py'</span>, <span class="st">'-v'</span>])</span></code></pre></div>
|
||||
<h3 id="choosing-enough-properties">Choosing “enough” properties</h3>
|
||||
<p>The property test expressed in <code>test_num_evens_one_more_even</code> is pretty neat, but it by itself is not sufficient to verify the correctness of the <code>num_evens</code> function. For example, this property would also hold true if <code>num_evens</code> simply returned the length of the list, rather than the number of even elements.</p>
|
||||
<p>This is drawback with property-based tests: even though we can now check some property for very many inputs automatically, a single property alone does not guarantee that a function is correct. The ideal goal of property-based testing, then, is <em>choosing properties to verify</em>, so that if all of the properties are verified, then the function must be correct. This sounds too good to be true, and it often is—as functions get more complex, it is challenging or even impossible to find such a set of properties.</p>
|
||||
<p>But for <code>num_evens</code>, a relatively simple function, it is actually possible to <em>formally prove</em> the following statement, which tells us exactly which properties we need to check.</p>
|
||||
<div class="framed fullwidth">
|
||||
<p><strong>Theorem (correctness for <code>num_evens</code>).</strong> An implementation for <code>num_evens</code> is correct (i.e., returns the number of even elements for any list of numbers) <em>if and only if</em> it satisfies all three of the following:</p>
|
||||
<ol type="1">
|
||||
<li><span class="math inline">\(\text{num_evens}(\text{[]}) = 0\)</span></li>
|
||||
<li><span class="math inline">\(\forall \text{nums} \in \mathcal{L}_{\text{int}},~ \forall x \in \Z,~ \text{num_evens}(\text{nums} + [2x]) = \text{num_evens}(\text{nums}) + 1\)</span></li>
|
||||
<li><span class="math inline">\(\forall \text{nums} \in \mathcal{L}_{\text{int}},~ \forall x \in \Z,~ \text{num_evens}(\text{nums} + [2x + 1]) = \text{num_evens}(\text{nums})\)</span></li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>Proving such a statement is beyond the scope of this chapter, but if you’re curious it is closely related to the proof technique of <em>induction</em>, which we will cover formally later this year. But the actual statement is pretty amazing: it tells us that with just one unit test (for <code>nums = []</code>) and two property tests, we can be certain that our <code>num_evens</code> function is correct!</p>
|
||||
<!--
|
||||
Let us consider a more complicated example, this time involving sets rather than integers.
|
||||
Let A and B be two arbitrary sets, and define for any set X, n(X) = "the number of elements in X".
|
||||
From this we can recall the following property:
|
||||
|
||||
$n(A \cup B) = n(A) + n(B) - n(A \cap B)$.
|
||||
|
||||
Let's add two functions to our `my_functions.py` file:
|
||||
|
||||
```python
|
||||
def union(set1: set, set2: set) -> set:
|
||||
"""Return a new set that is the union of set1 and set2.
|
||||
|
||||
>>> union({1, 2, 3}, set())
|
||||
{1, 2, 3}
|
||||
>>> union({4, 5, 6}, {4, 5, 6})
|
||||
{4, 5, 6}
|
||||
"""
|
||||
union_set = set1
|
||||
for item in set2:
|
||||
if item not in union_set:
|
||||
union_set.add(item)
|
||||
return union_set
|
||||
|
||||
def intersection(set1: set, set2: set) -> set:
|
||||
"""Return a new set that is the intersection of set1 and set2.
|
||||
|
||||
>>> intersection(set(), {7, 8, 9})
|
||||
set()
|
||||
>>> intersection({1, 2, 3, 'four'}, {2, 3, 4, 'four'})
|
||||
{2, 3, 'four'}
|
||||
"""
|
||||
intersection_set = set()
|
||||
for item in set1:
|
||||
if item in set2:
|
||||
intersection_set.add(item)
|
||||
return intersection_set
|
||||
```
|
||||
|
||||
Next, we will import these functions and test the earlier property by generating sets with hypothesis.
|
||||
The elements in the sets don't actually matter, so without loss of generality we will use integers in the sets:
|
||||
|
||||
```python
|
||||
from hypothesis import given
|
||||
# Note the change: we also import sets
|
||||
from hypothesis.strategies import integers, sets
|
||||
|
||||
from my_functions import union, intersection
|
||||
|
||||
@given(sets(integers()), sets(integers()))
|
||||
def test_intersection_formula(A: set, B: set):
|
||||
assert len(union(A, B)) == len(A) + len(B) - len(intersection(A, B))
|
||||
```
|
||||
|
||||
Note that our `@given` decorator has two arguments matching the two arguments of `test_intersection_formula`.
|
||||
Also notice that the `sets` strategy takes an argument itself.
|
||||
In this case, we have used `sets(integers())` to indicate that we want a set of integers for both `A` and `B`.
|
||||
The assertion will test to see that our property holds for all each `A` and `B`.
|
||||
Because we are using the `@given` decorator, the `hypothesis` module will test many different sets of integers to ensure that the property holds.
|
||||
|
||||
The `hypothesis` module has many strategies that can be used to generate different types of data.
|
||||
See the appendix for more information. -->
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,465 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>3.11 Working with Multiple Quantifiers</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="../tufte.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div style="display:none">
|
||||
\(
|
||||
\newcommand{\NOT}{\neg}
|
||||
\newcommand{\AND}{\wedge}
|
||||
\newcommand{\OR}{\vee}
|
||||
\newcommand{\XOR}{\oplus}
|
||||
\newcommand{\IMP}{\Rightarrow}
|
||||
\newcommand{\IFF}{\Leftrightarrow}
|
||||
\newcommand{\TRUE}{\text{True}\xspace}
|
||||
\newcommand{\FALSE}{\text{False}\xspace}
|
||||
\newcommand{\IN}{\,{\in}\,}
|
||||
\newcommand{\NOTIN}{\,{\notin}\,}
|
||||
\newcommand{\TO}{\rightarrow}
|
||||
\newcommand{\DIV}{\mid}
|
||||
\newcommand{\NDIV}{\nmid}
|
||||
\newcommand{\MOD}[1]{\pmod{#1}}
|
||||
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
|
||||
\newcommand{\N}{\mathbb N}
|
||||
\newcommand{\Z}{\mathbb Z}
|
||||
\newcommand{\Q}{\mathbb Q}
|
||||
\newcommand{\R}{\mathbb R}
|
||||
\newcommand{\C}{\mathbb C}
|
||||
\newcommand{\cA}{\mathcal A}
|
||||
\newcommand{\cB}{\mathcal B}
|
||||
\newcommand{\cC}{\mathcal C}
|
||||
\newcommand{\cD}{\mathcal D}
|
||||
\newcommand{\cE}{\mathcal E}
|
||||
\newcommand{\cF}{\mathcal F}
|
||||
\newcommand{\cG}{\mathcal G}
|
||||
\newcommand{\cH}{\mathcal H}
|
||||
\newcommand{\cI}{\mathcal I}
|
||||
\newcommand{\cJ}{\mathcal J}
|
||||
\newcommand{\cL}{\mathcal L}
|
||||
\newcommand{\cK}{\mathcal K}
|
||||
\newcommand{\cN}{\mathcal N}
|
||||
\newcommand{\cO}{\mathcal O}
|
||||
\newcommand{\cP}{\mathcal P}
|
||||
\newcommand{\cQ}{\mathcal Q}
|
||||
\newcommand{\cS}{\mathcal S}
|
||||
\newcommand{\cT}{\mathcal T}
|
||||
\newcommand{\cV}{\mathcal V}
|
||||
\newcommand{\cW}{\mathcal W}
|
||||
\newcommand{\cZ}{\mathcal Z}
|
||||
\newcommand{\emp}{\emptyset}
|
||||
\newcommand{\bs}{\backslash}
|
||||
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
|
||||
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
|
||||
\newcommand{\abs}[1]{\left | #1 \right |}
|
||||
\newcommand{\xspace}{}
|
||||
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
|
||||
\)
|
||||
</div>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">3.11 Working with Multiple Quantifiers</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>Expressions in predicate logic with a single quantifier can generally be translated into English as either “there exists an element <span class="math inline">\(x\)</span> of set <span class="math inline">\(S\)</span> that satisfies <span class="math inline">\(P(x)\)</span>” (existential quantifier) or “every element <span class="math inline">\(x\)</span> of set <span class="math inline">\(S\)</span> satisfies <span class="math inline">\(P(x)\)</span>” (universal quantifier). However, there are situations where multiple variables are quantified, and we need to pay special attention to what such statements are actually saying. Let us revisit our <span class="math inline">\(Loves\)</span> predicate from earlier this chapter. In particular, recall the following relationships regarding who loves whom:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th></th>
|
||||
<th style="text-align: left;">Sophia</th>
|
||||
<th style="text-align: left;">Thelonious</th>
|
||||
<th style="text-align: left;">Stanley</th>
|
||||
<th style="text-align: left;">Laura</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td>Breanna</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Malena</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Patrick</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Ella</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Remember that our <span class="math inline">\(Loves\)</span> predicate is binary—what if we wanted to quantify <em>both</em> of its inputs? Consider the formula: <span class="math display">\[\forall a \in A,~\forall b \in B,~Loves(a,b).\]</span></p>
|
||||
<p>We translate this as “for every person <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span>, for every person <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span>, <span class="math inline">\(a\)</span> loves <span class="math inline">\(b\)</span>.” After some thought, we notice that the order in which we quantified <span class="math inline">\(a\)</span> and <span class="math inline">\(b\)</span> doesn’t matter; the statement “for every person <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span>, for every person <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span>, <span class="math inline">\(a\)</span> loves <span class="math inline">\(b\)</span>” means exactly the same thing! In both cases, we are considering all possible pairs of people (one from <span class="math inline">\(A\)</span> and one from <span class="math inline">\(B\)</span>).</p>
|
||||
<p>In general, when we have two consecutive universal quantifiers the order does <em>not</em> matter. That is, the following two formulas are equivalent:<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote">Tip: when the domains of the two variables are the same, we typically combine the quantifications, e.g., <span class="math inline">\(\forall x \in S,~ \forall y \in S,~ P(x, y)\)</span> into <span class="math inline">\(\forall x,y \in S,~ P(x, y)\)</span>.</span></p>
|
||||
<ul>
|
||||
<li><span class="math inline">\(\forall x \in S_1,~ \forall y \in S_2,~ P(x, y)\)</span></li>
|
||||
<li><span class="math inline">\(\forall y \in S_2,~ \forall x \in S_1,~ P(x, y)\)</span></li>
|
||||
</ul>
|
||||
<p>The same is true of two consecutive existential quantifiers. Consider the statements “there exist an <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span> and <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span> such that <span class="math inline">\(a\)</span> loves <span class="math inline">\(b\)</span>” and “there exist a <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span> and <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span> such that <span class="math inline">\(a\)</span> loves <span class="math inline">\(b\)</span>.” Again, they mean the same thing: in this case, we only care about one particular pair of people (one from <span class="math inline">\(A\)</span> and one from <span class="math inline">\(B\)</span>), so the order in which we pick the particular <span class="math inline">\(a\)</span> and <span class="math inline">\(b\)</span> doesn’t matter. In general, the following two formulas are equivalent:</p>
|
||||
<ul>
|
||||
<li><span class="math inline">\(\exists x \in S_1,~ \exists y \in S_2,~ P(x, y)\)</span></li>
|
||||
<li><span class="math inline">\(\exists y \in S_2,~ \exists x \in S_1,~ P(x, y)\)</span></li>
|
||||
</ul>
|
||||
<p>But even though consecutive quantifiers of the same type behave very nicely, this is <strong>not</strong> the case for a pair of alternating quantifiers. First, consider <span class="math display">\[\forall a \in A,~ \exists b \in B,~ Loves(a,b).\]</span> This can be translated as “For every person <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span>, there exists a person <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span>, such that <span class="math inline">\(a\)</span> loves <span class="math inline">\(b\)</span>.”<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Or put a bit more naturally, “For every person <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span>, <span class="math inline">\(a\)</span> loves someone in <span class="math inline">\(B\)</span>,” which can be shortened even further to “Everyone in <span class="math inline">\(A\)</span> loves someone in <span class="math inline">\(B\)</span>.”</span> This is true: every person in <span class="math inline">\(A\)</span> loves at least one person.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: left;"><span class="math inline">\(a\)</span> (from <span class="math inline">\(A\)</span>)</th>
|
||||
<th style="text-align: left;"><span class="math inline">\(b\)</span> (a person in <span class="math inline">\(B\)</span> who <span class="math inline">\(a\)</span> loves)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">Breanna</td>
|
||||
<td style="text-align: left;">Thelonious</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: left;">Malena</td>
|
||||
<td style="text-align: left;">Laura</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">Patrick</td>
|
||||
<td style="text-align: left;">Stanley</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: left;">Ella</td>
|
||||
<td style="text-align: left;">Stanley</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Note that the choice of person who <span class="math inline">\(a\)</span> loves depends on <span class="math inline">\(a\)</span>: this is consistent with the latter part of the English translation, “<span class="math inline">\(a\)</span> loves someone in <span class="math inline">\(B\)</span>.”</p>
|
||||
<p>Let us contrast this with the similar-looking formula, where the order of the quantifiers has changed: <span class="math display">\[\exists b \in B,~ \forall a \in A,~ Loves(a,b).\]</span> This formula’s meaning is quite different: “there exists a person <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span>, where for every person <span class="math inline">\(a\)</span> in <span class="math inline">\(A\)</span>, <span class="math inline">\(a\)</span> loves <span class="math inline">\(b\)</span>.” Put more naturally, “there is a person <span class="math inline">\(b\)</span> in <span class="math inline">\(B\)</span> who is loved by everyone in <span class="math inline">\(A\)</span>” or “someone in <span class="math inline">\(B\)</span> is loved by everyone in <span class="math inline">\(A\)</span>”.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: left;"><span class="math inline">\(b\)</span> (from <span class="math inline">\(B\)</span>)</th>
|
||||
<th style="text-align: left;">Loved by everyone in <span class="math inline">\(A\)</span>?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">Sophia</td>
|
||||
<td style="text-align: left;">No</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: left;">Thelonious</td>
|
||||
<td style="text-align: left;">No</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">Stanley</td>
|
||||
<td style="text-align: left;">Yes</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: left;">Laura</td>
|
||||
<td style="text-align: left;">No</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>This happens to be True because everyone in <span class="math inline">\(A\)</span> loves Stanley. But it would <em>not</em> be True if we, for example, removed the love connection between Malena and Stanley. In this case, Stanley would no longer be loved by everyone, and so <em>no one</em> in <span class="math inline">\(B\)</span> is loved by everyone in <span class="math inline">\(A\)</span>. But notice that even if Malena no longer loves Stanley, the previous statement (“everyone in <span class="math inline">\(A\)</span> loves someone”) remains True!</p>
|
||||
<p>So we would have a case where switching the order of quantifiers changes the meaning of a formula! In both cases, the existential quantifier <span class="math inline">\(\exists b \in B\)</span> involves making a <em>choice</em> of person from <span class="math inline">\(B\)</span>. But in the first case, this quantifier occurs after <span class="math inline">\(a\)</span> is quantified, so the choice of <span class="math inline">\(b\)</span> is allowed to depend on the choice of <span class="math inline">\(a\)</span>. In the second case, this quantifier occurs before <span class="math inline">\(a\)</span>, and so the choice of <span class="math inline">\(b\)</span> must be <em>independent</em> of the choice of <span class="math inline">\(a\)</span>.</p>
|
||||
<p>When reading a nested quantified expression, you should read it from left to right, and pay attention to the order of the quantifiers. In order to see if the statement is True, whenever you come across a universal quantifier, you must verify the statement for every single value that this variable can take on. Whenever you see an existential quantifier, you only need to exhibit <em>one</em> value for that variable such that the statement is True, and this value can depend on the variables to the left of it, but not on the variables to the right of it.</p>
|
||||
<h2 id="translating-multiple-quantifiers-into-python-code">Translating multiple quantifiers into Python code</h2>
|
||||
<p>Now let’s see how we could represent this example in Python. First, recall the table of who loves whom from above:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th></th>
|
||||
<th style="text-align: left;">Sophia</th>
|
||||
<th style="text-align: left;">Thelonious</th>
|
||||
<th style="text-align: left;">Stanley</th>
|
||||
<th style="text-align: left;">Laura</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td>Breanna</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Malena</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Patrick</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Ella</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">False</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
<td style="text-align: left;">True</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>And we can represent this table of who loves whom in Python as a <em>list of lists</em> or, more precisely, using a <code>list[list[bool]]</code>.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a>[</span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">True</span>],</span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>]</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a>]</span></code></pre></div>
|
||||
<p>Our list is the same as the table above, except with the people’s names removed. Each <em>row</em> of the table represents a person from set <span class="math inline">\(A\)</span>, while each <em>column</em> in the table represents a person from set <span class="math inline">\(B\)</span>. We’ve kept the order the same; so the first row represents <span class="math inline">\(Breanna\)</span>, while the third column represents <span class="math inline">\(Stanley\)</span>.</p>
|
||||
<p>Now, how are we going to access the data from this table? For this section we’re going to put all of our work into a new file called <code>loves.py</code>, and so we’ll start by defining a new variable in this file:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="co"># In loves.py</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a>LOVES_TABLE <span class="op">=</span> [</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">True</span>],</span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>]</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a>]</span></code></pre></div>
|
||||
<p>This is the first time we’ve defined a variable within a Python file (rather than the Python console) that is <em>not</em> in a function definition. Variables defined in this way are called <em>global constants</em>, to distinguish them from the local variables defined within functions.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> The term “constant” is not important right now, but will become important later in the course.</span> Global constants are called “global” because their <em>scope</em> is the entire Python module in which they are defined: they can be accessed anywhere in the file, including all function bodies. They can also be imported and used in other Python modules, and are available when we run the file in the Python console.</p>
|
||||
<h3 id="exploring-loves_table">Exploring <code>LOVES_TABLE</code></h3>
|
||||
<p>To start, let’s run our <code>loves.py</code> file in the Python console so we can play around with the <code>LOVES_TABLE</code> value. Because <code>LOVES_TABLE</code> is a list of lists, where each inner list represents a row of the table, it’s easy to access a single row with list indexing:</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">>>></span> LOVES_TABLE[<span class="dv">0</span>] <span class="co"># This is the first row of the table</span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a>[<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">False</span>]</span></code></pre></div>
|
||||
<p>From here, we can access individual elements of the table, which represent an individual value of the <span class="math inline">\(Loves(a, b)\)</span> predicate.</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="op">>>></span> LOVES_TABLE[<span class="dv">0</span>][<span class="dv">1</span>] <span class="co"># This is the (0, 1) entry in the table</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><span class="va">True</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a><span class="op">>>></span> LOVES_TABLE[<span class="dv">2</span>][<span class="dv">3</span>] <span class="co"># This is the (2, 3) entry in the table</span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a><span class="va">False</span></span></code></pre></div>
|
||||
<p>In general, <code>LOVES_TABLE[i][j]</code> evaluates to the entry in row <code>i</code> and column <code>j</code> of the table. Finally, since the data is stored by rows, accessing columns is a little more work. To access column <code>j</code>, we can use a list comprehension to access the <code>j</code>-th element in each row:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="op">>>></span> [LOVES_TABLE[i][<span class="dv">0</span>] <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="dv">4</span>)]</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a>[<span class="va">False</span>, <span class="va">False</span>, <span class="va">False</span>, <span class="va">False</span>]</span></code></pre></div>
|
||||
<p>Now, let’s return to our Python file <code>loves.py</code> and define a version of our <span class="math inline">\(Loves\)</span> predicate. First, we add two more constants to represent the sets <span class="math inline">\(A\)</span> and <span class="math inline">\(B\)</span>, but using a dictionary to map names to their corresponding indices in <code>LOVES_TABLE</code>.</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="co"># In loves.py</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a>LOVES_TABLE <span class="op">=</span> [</span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">True</span>],</span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>]</span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a>]</span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a></span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a>A <span class="op">=</span> {</span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> <span class="st">'Breanna'</span>: <span class="dv">0</span>,</span>
|
||||
<span id="cb6-11"><a href="#cb6-11"></a> <span class="st">'Malena'</span>: <span class="dv">1</span>,</span>
|
||||
<span id="cb6-12"><a href="#cb6-12"></a> <span class="st">'Patrick'</span>: <span class="dv">2</span>,</span>
|
||||
<span id="cb6-13"><a href="#cb6-13"></a> <span class="st">'Ella'</span>: <span class="dv">3</span></span>
|
||||
<span id="cb6-14"><a href="#cb6-14"></a>}</span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a></span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a>B <span class="op">=</span> {</span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a> <span class="st">'Sophia'</span>: <span class="dv">0</span>,</span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a> <span class="st">'Thelonius'</span>: <span class="dv">1</span>,</span>
|
||||
<span id="cb6-19"><a href="#cb6-19"></a> <span class="st">'Stanley'</span>: <span class="dv">2</span>,</span>
|
||||
<span id="cb6-20"><a href="#cb6-20"></a> <span class="st">'Laura'</span>: <span class="dv">3</span>,</span>
|
||||
<span id="cb6-21"><a href="#cb6-21"></a>}</span></code></pre></div>
|
||||
<p>Next, we define a <code>loves</code> predicate, which take in two strings (note the preconditions) and returns whether person <code>a</code> loves person <code>b</code>.<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> Note that because this function is defined in the same file as <code>LOVES_TABLE</code>, it can access that global constant in its body.</span></p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> loves(a: <span class="bu">str</span>, b: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">"""Return whether the person at index a loves the person at index b.</span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> - a in A</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a><span class="co"> - b in B</span></span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a></span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a><span class="co"> >>> loves('Breanna', 'Sophia')</span></span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a><span class="co"> False</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a><span class="co"> """</span></span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a> a_index <span class="op">=</span> A[a]</span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a> b_index <span class="op">=</span> B[b]</span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a> <span class="cf">return</span> LOVES_TABLE[a_index][b_index]</span></code></pre></div>
|
||||
<p>Now that we’ve seen how to access individual entries, rows, and columns from the table, let’s turn to how we would represent the statements in predicate logic we’ve written in this section. First, we can express <span class="math inline">\(\forall a \in A,~ \forall b \in B,~ Loves(a, b)\)</span> as the expression:</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">>>></span> <span class="bu">all</span>({loves(a, b) <span class="cf">for</span> a <span class="kw">in</span> A <span class="cf">for</span> b <span class="kw">in</span> B})</span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a><span class="va">False</span></span></code></pre></div>
|
||||
<p>And similarly, we can express <span class="math inline">\(\exists a \in A,~ \exists b \in B,~ Loves(a, b)\)</span> as the expression:</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="op">>>></span> <span class="bu">any</span>({loves(a, b) <span class="cf">for</span> a <span class="kw">in</span> A <span class="cf">for</span> b <span class="kw">in</span> B})</span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>These two examples illustrate how Python’s <code>all</code> and <code>any</code> functions naturally enable us to express multiple quantifiers of the same type. But what about the expressions we looked at with alternating quantifiers? Consider <span class="math inline">\(\forall a \in A,~ \exists b \in B,~ Loves(a,b)\)</span>. It is possible to construct a nested expression that represents this one as well:</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="op">>>></span> <span class="bu">all</span>({<span class="bu">any</span>({loves(a, b) <span class="cf">for</span> b <span class="kw">in</span> B}) <span class="cf">for</span> a <span class="kw">in</span> A})</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>Though this is structurally equivalent to the statement in predicate logic, it’s syntactically longer and a bit harder to read. In general we try to avoid lots of nesting in expressions in programming, and a rule of thumb we’ll try to follow in this course is to <em>never nest <code>all</code>/<code>any</code> calls</em>. Instead, we can pull out the inner <code>any</code> into its own function, which not only reduces the nesting but makes it clearer what’s going on:</p>
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="kw">def</span> loves_someone(a: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a> <span class="co">"""Return whether a loves at least one person in B.</span></span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a></span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a><span class="co"> - a in A</span></span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb11-7"><a href="#cb11-7"></a> <span class="cf">return</span> <span class="bu">any</span>({loves(a, b) <span class="cf">for</span> b <span class="kw">in</span> B})</span>
|
||||
<span id="cb11-8"><a href="#cb11-8"></a></span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a></span>
|
||||
<span id="cb11-10"><a href="#cb11-10"></a><span class="op">>>></span> <span class="bu">all</span>({loves_someone(a) <span class="cf">for</span> a <span class="kw">in</span> A})</span>
|
||||
<span id="cb11-11"><a href="#cb11-11"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>Similarly, we can express the statement <span class="math inline">\(\exists b \in B,~ \forall a \in A,~ Loves(a,b)\)</span> in two different ways. With a nested <code>any</code>/<code>all</code>:</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="op">>>></span> <span class="bu">any</span>({<span class="bu">all</span>({loves(a, b)} <span class="cf">for</span> a <span class="kw">in</span> A) <span class="cf">for</span> b <span class="kw">in</span> B})</span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>And by pulling out the inner <code>all</code> expression into a named function:</p>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">def</span> loved_by_everyone(b: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb13-2"><a href="#cb13-2"></a> <span class="co">"""Return whether b is loved by everyone in A.</span></span>
|
||||
<span id="cb13-3"><a href="#cb13-3"></a></span>
|
||||
<span id="cb13-4"><a href="#cb13-4"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb13-5"><a href="#cb13-5"></a><span class="co"> - b in B</span></span>
|
||||
<span id="cb13-6"><a href="#cb13-6"></a><span class="co"> """</span></span>
|
||||
<span id="cb13-7"><a href="#cb13-7"></a> <span class="cf">return</span> <span class="bu">all</span>({loves(a, b)} <span class="cf">for</span> a <span class="kw">in</span> A)</span>
|
||||
<span id="cb13-8"><a href="#cb13-8"></a></span>
|
||||
<span id="cb13-9"><a href="#cb13-9"></a></span>
|
||||
<span id="cb13-10"><a href="#cb13-10"></a><span class="op">>>></span> <span class="bu">any</span>({loved_by_everyone(b) <span class="cf">for</span> b <span class="kw">in</span> B})</span>
|
||||
<span id="cb13-11"><a href="#cb13-11"></a><span class="va">True</span></span></code></pre></div>
|
||||
<p>Here is our final <code>loves.py</code> file, for you to play around with:</p>
|
||||
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1"></a><span class="co"># In loves.py</span></span>
|
||||
<span id="cb14-2"><a href="#cb14-2"></a>LOVES_TABLE <span class="op">=</span> [</span>
|
||||
<span id="cb14-3"><a href="#cb14-3"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb14-4"><a href="#cb14-4"></a> [<span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>, <span class="va">True</span>],</span>
|
||||
<span id="cb14-5"><a href="#cb14-5"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">False</span>],</span>
|
||||
<span id="cb14-6"><a href="#cb14-6"></a> [<span class="va">False</span>, <span class="va">False</span>, <span class="va">True</span>, <span class="va">True</span>]</span>
|
||||
<span id="cb14-7"><a href="#cb14-7"></a>]</span>
|
||||
<span id="cb14-8"><a href="#cb14-8"></a></span>
|
||||
<span id="cb14-9"><a href="#cb14-9"></a>A <span class="op">=</span> {</span>
|
||||
<span id="cb14-10"><a href="#cb14-10"></a> <span class="st">'Breanna'</span>: <span class="dv">0</span>,</span>
|
||||
<span id="cb14-11"><a href="#cb14-11"></a> <span class="st">'Malena'</span>: <span class="dv">1</span>,</span>
|
||||
<span id="cb14-12"><a href="#cb14-12"></a> <span class="st">'Patrick'</span>: <span class="dv">2</span>,</span>
|
||||
<span id="cb14-13"><a href="#cb14-13"></a> <span class="st">'Ella'</span>: <span class="dv">3</span></span>
|
||||
<span id="cb14-14"><a href="#cb14-14"></a>}</span>
|
||||
<span id="cb14-15"><a href="#cb14-15"></a></span>
|
||||
<span id="cb14-16"><a href="#cb14-16"></a>B <span class="op">=</span> {</span>
|
||||
<span id="cb14-17"><a href="#cb14-17"></a> <span class="st">'Sophia'</span>: <span class="dv">0</span>,</span>
|
||||
<span id="cb14-18"><a href="#cb14-18"></a> <span class="st">'Thelonius'</span>: <span class="dv">1</span>,</span>
|
||||
<span id="cb14-19"><a href="#cb14-19"></a> <span class="st">'Stanley'</span>: <span class="dv">2</span>,</span>
|
||||
<span id="cb14-20"><a href="#cb14-20"></a> <span class="st">'Laura'</span>: <span class="dv">3</span>,</span>
|
||||
<span id="cb14-21"><a href="#cb14-21"></a>}</span>
|
||||
<span id="cb14-22"><a href="#cb14-22"></a></span>
|
||||
<span id="cb14-23"><a href="#cb14-23"></a><span class="kw">def</span> loves(a: <span class="bu">str</span>, b: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb14-24"><a href="#cb14-24"></a> <span class="co">"""Return whether the person at index a loves the person at index b.</span></span>
|
||||
<span id="cb14-25"><a href="#cb14-25"></a></span>
|
||||
<span id="cb14-26"><a href="#cb14-26"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb14-27"><a href="#cb14-27"></a><span class="co"> - a in A</span></span>
|
||||
<span id="cb14-28"><a href="#cb14-28"></a><span class="co"> - b in B</span></span>
|
||||
<span id="cb14-29"><a href="#cb14-29"></a></span>
|
||||
<span id="cb14-30"><a href="#cb14-30"></a><span class="co"> >>> loves('Breanna', 'Sophia')</span></span>
|
||||
<span id="cb14-31"><a href="#cb14-31"></a><span class="co"> False</span></span>
|
||||
<span id="cb14-32"><a href="#cb14-32"></a><span class="co"> """</span></span>
|
||||
<span id="cb14-33"><a href="#cb14-33"></a> a_index <span class="op">=</span> A[a]</span>
|
||||
<span id="cb14-34"><a href="#cb14-34"></a> b_index <span class="op">=</span> B[b]</span>
|
||||
<span id="cb14-35"><a href="#cb14-35"></a> <span class="cf">return</span> LOVES_TABLE[a_index][b_index]</span>
|
||||
<span id="cb14-36"><a href="#cb14-36"></a></span>
|
||||
<span id="cb14-37"><a href="#cb14-37"></a></span>
|
||||
<span id="cb14-38"><a href="#cb14-38"></a><span class="kw">def</span> loves_someone(a: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb14-39"><a href="#cb14-39"></a> <span class="co">"""Return whether a loves at least one person in B.</span></span>
|
||||
<span id="cb14-40"><a href="#cb14-40"></a></span>
|
||||
<span id="cb14-41"><a href="#cb14-41"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb14-42"><a href="#cb14-42"></a><span class="co"> - a in A</span></span>
|
||||
<span id="cb14-43"><a href="#cb14-43"></a><span class="co"> """</span></span>
|
||||
<span id="cb14-44"><a href="#cb14-44"></a> <span class="cf">return</span> <span class="bu">any</span>({loves(a, b) <span class="cf">for</span> b <span class="kw">in</span> B})</span>
|
||||
<span id="cb14-45"><a href="#cb14-45"></a></span>
|
||||
<span id="cb14-46"><a href="#cb14-46"></a></span>
|
||||
<span id="cb14-47"><a href="#cb14-47"></a><span class="kw">def</span> loved_by_everyone(b: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">bool</span>:</span>
|
||||
<span id="cb14-48"><a href="#cb14-48"></a> <span class="co">"""Return whether b is loved by everyone in A.</span></span>
|
||||
<span id="cb14-49"><a href="#cb14-49"></a></span>
|
||||
<span id="cb14-50"><a href="#cb14-50"></a><span class="co"> Preconditions:</span></span>
|
||||
<span id="cb14-51"><a href="#cb14-51"></a><span class="co"> - b in B</span></span>
|
||||
<span id="cb14-52"><a href="#cb14-52"></a><span class="co"> """</span></span>
|
||||
<span id="cb14-53"><a href="#cb14-53"></a> <span class="cf">return</span> <span class="bu">all</span>({loves(a, b)} <span class="cf">for</span> a <span class="kw">in</span> A)</span></code></pre></div>
|
||||
<h2 id="a-famous-logical-statement">A famous logical statement</h2>
|
||||
<p>Before we wrap up, let us use our understanding of multiple quantifiers to express one of the more famous properties about prime numbers: “there are infinitely many primes.”<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote"> Later on, we’ll actually prove this statement! </span></p>
|
||||
<p>In <a href="09-working-with-definitions.html">Section 2.9</a>, we saw how to express the fact that a single number <span class="math inline">\(p\)</span> is a prime number, but how do we capture “infinitely many”? The key idea is that because primes are natural numbers, if there are infinitely many of them, then they have to keep growing bigger and bigger.<label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote"> Another way to think about this is to consider the statement “every prime number is less than 9000. If this statement were True, then there could only be at most 8999 primes.” </span> So we can express the original statement as “every natural number has a prime number larger than it,” or in the symbolic notation: <span class="math display">\[\forall n \in \N,~ \exists p \in \N,~ p > n \land IsPrime(p).\]</span></p>
|
||||
<p>If we wanted to express this statement without either the <span class="math inline">\(IsPrime\)</span> or divisibility predicates, we would end up with an extremely cumbersome statement: <span class="math display">\[\forall n \in \N,~ \exists p \IN \N,~
|
||||
p > n \land
|
||||
p > 1 \land \Big( \forall d \in \N,~ \left(\exists k \in \Z,~ p = kd\right) \Rightarrow d = 1 \OR d = p \Big).\]</span></p>
|
||||
<p>This statement is terribly ugly, which is why we define our own predicates! Keep this in mind throughout the course: when you are given a statement to express, make sure you are aware of all of the relevant definitions, and make use of them to simplify your expression.</p>
|
||||
<h2 id="looking-ahead">Looking Ahead</h2>
|
||||
<p>In this section, we’ve introduced the notion of lists within lists to represent tables of values for binary predicates. In the next chapter, we’ll start looking at tabular data and other forms of nested collections of data in more detail, and see how these more complex structures can be used to represent real-world data for our programs.</p>
|
||||
</section>
|
||||
<footer>
|
||||
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user