clarify Lecture 4 and Set 4

This commit is contained in:
Joel Kaasinen
2020-05-11 16:26:40 +03:00
parent cb0703e6b4
commit 8685c46448
2 changed files with 29 additions and 16 deletions
+2
View File
@@ -194,6 +194,8 @@ transfer from to amount bank = todo
-- Ex 11: given an Array, find the index of the largest element. You
-- can assume the Array isn't empty.
--
-- You may assume that the largest element is unique.
--
-- Hint: check out Data.Array.indices or Data.Array.assocs
maxIndex :: (Ix i, Ord a) => Array i a -> i
+27 -16
View File
@@ -1979,12 +1979,20 @@ Returns the second element of a list
<h2 id="interlude-folding"><span class="header-section-number">4.2</span> Interlude: folding</h2>
<!-- TODO: should this be in the same section as foldable? -->
<p>Consider the functions <code>sumNumbers :: [Int] -&gt; Int</code>, <code>myMaximum :: [Int] -&gt; Int</code>, and <code>countNothings :: [Maybe a] -&gt; Int</code> again.</p>
<p>```haskell sumNumbers :: [Int] -&gt; Int sumNumbers [] = 0 sumNumbers (x:xs) = x + sumNumbers xs</p>
<p>myMaximum :: [Int] -&gt; Int myMaximum [] = 0 myMaximum (x:xs) = go x xs where go biggest [] = biggest go biggest (x:xs) = go (max biggest x) xs</p>
<div class="sourceCode" id="cb222"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb222-1" data-line-number="1"><span class="ot">countNothings ::</span> [<span class="dt">Maybe</span> a] <span class="ot">-&gt;</span> <span class="dt">Int</span></a>
<a class="sourceLine" id="cb222-2" data-line-number="2">countNothings [] <span class="fu">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb222-3" data-line-number="3">countNothings (<span class="dt">Nothing</span> <span class="fu">:</span> xs) <span class="fu">=</span> <span class="dv">1</span> <span class="fu">+</span> countNothings xs</a>
<a class="sourceLine" id="cb222-4" data-line-number="4">countNothings (<span class="dt">Just</span> _ <span class="fu">:</span> xs) <span class="fu">=</span> countNothings xs</a></code></pre></div>
<div class="sourceCode" id="cb222"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb222-1" data-line-number="1"><span class="ot">sumNumbers ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></a>
<a class="sourceLine" id="cb222-2" data-line-number="2">sumNumbers [] <span class="fu">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb222-3" data-line-number="3">sumNumbers (x<span class="fu">:</span>xs) <span class="fu">=</span> x <span class="fu">+</span> sumNumbers xs</a>
<a class="sourceLine" id="cb222-4" data-line-number="4"></a>
<a class="sourceLine" id="cb222-5" data-line-number="5"><span class="ot">myMaximum ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></a>
<a class="sourceLine" id="cb222-6" data-line-number="6">myMaximum [] <span class="fu">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb222-7" data-line-number="7">myMaximum (x<span class="fu">:</span>xs) <span class="fu">=</span> go x xs</a>
<a class="sourceLine" id="cb222-8" data-line-number="8"> <span class="kw">where</span> go biggest [] <span class="fu">=</span> biggest</a>
<a class="sourceLine" id="cb222-9" data-line-number="9"> go biggest (x<span class="fu">:</span>xs) <span class="fu">=</span> go (max biggest x) xs</a>
<a class="sourceLine" id="cb222-10" data-line-number="10"></a>
<a class="sourceLine" id="cb222-11" data-line-number="11"><span class="ot">countNothings ::</span> [<span class="dt">Maybe</span> a] <span class="ot">-&gt;</span> <span class="dt">Int</span></a>
<a class="sourceLine" id="cb222-12" data-line-number="12">countNothings [] <span class="fu">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb222-13" data-line-number="13">countNothings (<span class="dt">Nothing</span> <span class="fu">:</span> xs) <span class="fu">=</span> <span class="dv">1</span> <span class="fu">+</span> countNothings xs</a>
<a class="sourceLine" id="cb222-14" data-line-number="14">countNothings (<span class="dt">Just</span> _ <span class="fu">:</span> xs) <span class="fu">=</span> countNothings xs</a></code></pre></div>
<p>They have one common characteristic. They take a list and produce a value that depends on the values of the elements in the given list. They “crunch” or <em>fold</em> a list of many values into a single value.</p>
<p>Prelude has a function called <code>foldr</code>, which performs a <em>right associative fold</em> over a <code>Foldable</code> data type. We’ll learn more about <code>Foldable</code> soon. At this point, it suffices to think of lists, so we define</p>
<div class="sourceCode" id="cb223"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb223-1" data-line-number="1">foldr<span class="ot"> ::</span> (a <span class="ot">-&gt;</span> b <span class="ot">-&gt;</span> b) <span class="ot">-&gt;</span> b <span class="ot">-&gt;</span> [a] <span class="ot">-&gt;</span> b</a>
@@ -2217,13 +2225,14 @@ Returns the second element of a list
<a class="sourceLine" id="cb261-2" data-line-number="2">Map.fromList<span class="ot"> ::</span> <span class="dt">Ord</span> k <span class="ot">=&gt;</span> [(k, a)] <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a</a>
<a class="sourceLine" id="cb261-3" data-line-number="3"></a>
<a class="sourceLine" id="cb261-4" data-line-number="4"><span class="co">-- Insert a value into a map. Overrides any previous value with the same key.</span></a>
<a class="sourceLine" id="cb261-5" data-line-number="5">Map.insert<span class="ot"> ::</span> <span class="dt">Ord</span> k <span class="ot">=&gt;</span> k <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a</a>
<a class="sourceLine" id="cb261-6" data-line-number="6"></a>
<a class="sourceLine" id="cb261-7" data-line-number="7"><span class="co">-- Get a value from a map using a key. Returns Nothing if the key was not present in the map.</span></a>
<a class="sourceLine" id="cb261-8" data-line-number="8">Map.lookup<span class="ot"> ::</span> <span class="dt">Ord</span> k <span class="ot">=&gt;</span> k <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a <span class="ot">-&gt;</span> <span class="dt">Maybe</span> a</a>
<a class="sourceLine" id="cb261-9" data-line-number="9"></a>
<a class="sourceLine" id="cb261-10" data-line-number="10"><span class="co">-- An empty map</span></a>
<a class="sourceLine" id="cb261-11" data-line-number="11">Map.empty<span class="ot"> ::</span> <span class="dt">Map.Map</span> k a</a></code></pre></div>
<a class="sourceLine" id="cb261-5" data-line-number="5"><span class="co">-- Returns a new map. Does not mutate the given map.</span></a>
<a class="sourceLine" id="cb261-6" data-line-number="6">Map.insert<span class="ot"> ::</span> <span class="dt">Ord</span> k <span class="ot">=&gt;</span> k <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a</a>
<a class="sourceLine" id="cb261-7" data-line-number="7"></a>
<a class="sourceLine" id="cb261-8" data-line-number="8"><span class="co">-- Get a value from a map using a key. Returns Nothing if the key was not present in the map.</span></a>
<a class="sourceLine" id="cb261-9" data-line-number="9">Map.lookup<span class="ot"> ::</span> <span class="dt">Ord</span> k <span class="ot">=&gt;</span> k <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> k a <span class="ot">-&gt;</span> <span class="dt">Maybe</span> a</a>
<a class="sourceLine" id="cb261-10" data-line-number="10"></a>
<a class="sourceLine" id="cb261-11" data-line-number="11"><span class="co">-- An empty map</span></a>
<a class="sourceLine" id="cb261-12" data-line-number="12">Map.empty<span class="ot"> ::</span> <span class="dt">Map.Map</span> k a</a></code></pre></div>
<p>The <code>Ord</code> constraint for the key type of the map is needed because maps are implemented as <em>ordered binary search trees</em>.</p>
<p>Here’s an example of representing a bank as a <code>Map String Int</code> (map from account name to account balance), and withdrawing some money from an account:</p>
<div class="sourceCode" id="cb262"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb262-1" data-line-number="1"><span class="ot">withdraw ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> <span class="dt">String</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> <span class="dt">String</span> <span class="dt">Int</span></a>
@@ -2231,12 +2240,14 @@ Returns the second element of a list
<a class="sourceLine" id="cb262-3" data-line-number="3"> <span class="kw">case</span> Map.lookup account bank <span class="kw">of</span></a>
<a class="sourceLine" id="cb262-4" data-line-number="4"> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> bank <span class="co">-- account not found, no change</span></a>
<a class="sourceLine" id="cb262-5" data-line-number="5"> <span class="dt">Just</span> sum <span class="ot">-&gt;</span> Map.insert account (sum<span class="fu">-</span>amount) bank <span class="co">-- set new balance</span></a></code></pre></div>
<p>Here’s how you might use the <code>withdraw</code> function in <code>GHCi</code>. Note how the maps get printed as <code>fromList</code> invocations.</p>
<p>Here’s how you might use the <code>withdraw</code> function in <code>GHCi</code>. Note how the maps get printed as <code>fromList</code> invocations. Also note how calling <code>withdraw ... bank</code> returns a <em>new</em> bank and doesn’t change the existing bank. Maps are <em>immutable</em> just like other Haskell data structures like lists.</p>
<div class="sourceCode" id="cb263"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb263-1" data-line-number="1"><span class="dt">GHCi</span><span class="fu">&gt;</span> <span class="kw">let</span> bank <span class="fu">=</span> Map.fromList [(<span class="st">&quot;Bob&quot;</span>,<span class="dv">100</span>),(<span class="st">&quot;Mike&quot;</span>,<span class="dv">50</span>)]</a>
<a class="sourceLine" id="cb263-2" data-line-number="2"><span class="dt">GHCi</span><span class="fu">&gt;</span> withdraw <span class="st">&quot;Bob&quot;</span> <span class="dv">80</span> bank</a>
<a class="sourceLine" id="cb263-3" data-line-number="3">fromList [(<span class="st">&quot;Bob&quot;</span>,<span class="dv">20</span>),(<span class="st">&quot;Mike&quot;</span>,<span class="dv">50</span>)]</a>
<a class="sourceLine" id="cb263-4" data-line-number="4"><span class="dt">GHCi</span><span class="fu">&gt;</span> withdraw <span class="st">&quot;Bozo&quot;</span> <span class="dv">1000</span> bank</a>
<a class="sourceLine" id="cb263-5" data-line-number="5">fromList [(<span class="st">&quot;Bob&quot;</span>,<span class="dv">100</span>),(<span class="st">&quot;Mike&quot;</span>,<span class="dv">50</span>)]</a></code></pre></div>
<a class="sourceLine" id="cb263-5" data-line-number="5">fromList [(<span class="st">&quot;Bob&quot;</span>,<span class="dv">100</span>),(<span class="st">&quot;Mike&quot;</span>,<span class="dv">50</span>)]</a>
<a class="sourceLine" id="cb263-6" data-line-number="6"><span class="dt">GHCi</span><span class="fu">&gt;</span> bank</a>
<a class="sourceLine" id="cb263-7" data-line-number="7">fromList [(<span class="st">&quot;Bob&quot;</span>,<span class="dv">100</span>),(<span class="st">&quot;Mike&quot;</span>,<span class="dv">50</span>)]</a></code></pre></div>
<p><code>Data.Map</code> defines all sorts of useful higher-order functions for updating maps. We can rewrite the <code>withdraw</code> function using <code>Data.Map.adjust</code>:</p>
<div class="sourceCode" id="cb264"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb264-1" data-line-number="1"><span class="ot">withdraw ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> <span class="dt">String</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Map.Map</span> <span class="dt">String</span> <span class="dt">Int</span></a>
<a class="sourceLine" id="cb264-2" data-line-number="2">withdraw account amount bank <span class="fu">=</span> Map.adjust (\x <span class="ot">-&gt;</span> x<span class="fu">-</span>amount) account bank</a></code></pre></div>
@@ -2249,7 +2260,7 @@ Returns the second element of a list
<div class="sourceCode" id="cb266"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb266-1" data-line-number="1"><span class="ot">array ::</span> <span class="dt">Ix</span> i <span class="ot">=&gt;</span> (i, i) <span class="ot">-&gt;</span> [(i, e)] <span class="ot">-&gt;</span> <span class="dt">Array</span> i e</a></code></pre></div>
<p>There are a couple of things to notice here. First of all, the <code>Array</code> type is parameterized by <em>two</em> types: the index type and the element type. Most other programming languages only parameterize arrays with the element type, but the index type is always <code>int</code>. In Haskell, we can have, for example, an <code>Array Char Int</code>: an array indexed by characters, or <code>Array Bool String</code>, an array indexed by booleans, or even <code>Array (Int,Int) Int</code>, a two-dimensional array of ints.</p>
<p>Not all types can be index types. Only types that are similar to integers are suitable. That is the reason for the <code>Ix i</code> class constraint. The <code>Ix</code> class collects all the types that can be used as array indexes.</p>
<p>Secondly, the <code>array</code> function takes an extra <code>(i,i)</code> parameter. These are the minimun and maximum indexes of the array. Unlike in other languages, where arrays always start at index 0 or 1, in Haskell you can define an array that starts from 7 and goes to 11. So here’s that array:</p>
<p>Secondly, the <code>array</code> function takes an extra <code>(i,i)</code> parameter. These are the minimun and maximum indexes of the array. Unlike some other languages, where arrays always start at index 0 or 1, in Haskell you can define an array that starts from 7 and goes to 11. So here’s that array:</p>
<div class="sourceCode" id="cb267"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb267-1" data-line-number="1"><span class="ot">myArray ::</span> <span class="dt">Array</span> <span class="dt">Int</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb267-2" data-line-number="2">myArray <span class="fu">=</span> array (<span class="dv">7</span>,<span class="dv">11</span>) [(<span class="dv">7</span>,<span class="st">&quot;seven&quot;</span>), (<span class="dv">8</span>,<span class="st">&quot;eight&quot;</span>), (<span class="dv">9</span>,<span class="st">&quot;nine&quot;</span>), (<span class="dv">10</span>,<span class="st">&quot;ten&quot;</span>), (<span class="dv">11</span>,<span class="st">&quot;ELEVEN&quot;</span>)]</a></code></pre></div>
<p>Listing all the indices and elements in order can be a bit cumbersome, so there’s also the <code>listArray</code> constructor that just takes a list of elements in order:</p>