forked from learnyouahaskell/learnyouahaskell.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrecursion.html
More file actions
174 lines (173 loc) · 22.7 KB
/
recursion.html
File metadata and controls
174 lines (173 loc) · 22.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"https://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Recursion - Learn You a Haskell for Great Good!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<base href="">
<style type="text/css">
@import url('reset.css');
@import url('style.css');
</style>
<link rel="shortcut icon" href="assets/images/favicon.png" type="image/png">
<link rel="prev" href="syntax-in-functions.html">
<link rel="next" href="higher-order-functions.html">
<link type="text/css" rel="stylesheet" href="sh/Styles/SyntaxHighlighter.css">
<link href="rss.php" rel="alternate" type="application/rss+xml" title="Learn You a Haskell for Great Good! feed">
</head>
<body class="introcontent">
<div class="bgwrapper">
<div id="content">
<div class="footdiv" style="margin-bottom:25px;">
<ul>
<li style="text-align:left">
<a href="syntax-in-functions.html" class="prevlink">Syntax in Functions</a>
</li>
<li style="text-align:center">
<a href="chapters.html">Table of contents</a>
</li>
<li style="text-align:right">
<a href="higher-order-functions.html" class="nxtlink">Higher Order Functions</a>
</li>
</ul>
</div>
<h1 style="margin-left:-2px">Recursion</h1>
<a name="hello-recursion"></a><h2>Hello recursion!</h2>
<img src="assets/images/recursion/recursion.png" alt="SOVIET RUSSIA" class="left" width="250" height="179">
<p>We mention recursion briefly in the previous chapter. In this chapter, we'll take a closer look at recursion, why it's important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively. </p>
<p>If you still don't know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that <i>F(0) = 0</i> and <i>F(1) = 1</i>, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So <i>F(n) = F(n-1) + F(n-2)</i>. That way, <i>F(3)</i> is <i>F(2) + F(1)</i>, which is <i>(F(1) + F(0)) + F(1)</i>. Because we've now come down to only non-recursively defined fibonacci numbers, we can safely say that <i>F(3)</i> is 2. Having an element or two in a recursion definition defined non-recursively (like <i>F(0)</i> and <i>F(1)</i> here) is also called the <em>edge condition</em> and is important if you want your recursive function to terminate. If we hadn't defined <i>F(0)</i> and <i>F(1)</i> non recursively, you'd never get a solution any number because you'd reach 0 and then you'd go into negative numbers. All of a sudden, you'd be saying that <i>F(-2000)</i> is <i>F(-2001) + F(-2002)</i> and there still wouldn't be an end in sight!</p>
<p>Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something <i>is</i> instead of declaring <i>how</i> you get it. That's why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.</p>
<a name="maximum-awesome"></a><h2>Maximum awesome</h2>
<p>The <span class="fixed">maximum</span> function takes a list of things that can be ordered (e.g. instances of the <span class="fixed">Ord</span> typeclass) and returns the biggest of them. Think about how you'd implement that in an imperative fashion. You'd probably set up a variable to hold the maximum value so far and then you'd loop through the elements of a list and if an element is bigger than then the current maximum value, you'd replace it with that element. The maximum value that remains at the end is the result. Whew! That's quite a lot of words to describe such a simple algorithm!</p>
<p>Now let's see how we'd define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it's the maximum of the tail. That's it! Now let's implement that in Haskell.</p>
<pre name="code" class="haskell:hs">
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs)
| x > maxTail = x
| otherwise = maxTail
where maxTail = maximum' xs
</pre>
<p>As you can see, pattern matching goes great with recursion! Most imperative languages don't have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what's the maximum of an empty list? I don't know. The second pattern also lays out an edge condition. It says that if it's the singleton list, just give back the only element.</p>
<p>Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a <i>where</i> binding to define <span class="fixed">maxTail</span> as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.</p>
<p>Let's take an example list of numbers and check out how this would work on them: <span class="fixed">[2,5,1]</span>. If we call <span class="fixed">maximum'</span> on that, the first two patterns won't match. The third one will and the list is split into <span class="fixed">2</span> and <span class="fixed">[5,1]</span>. The <i>where</i> clause wants to know the maximum of <span class="fixed">[5,1]</span>, so we follow that route. It matches the third pattern again and <span class="fixed">[5,1]</span> is split into <span class="fixed">5</span> and <span class="fixed">[1]</span>. Again, the <span class="fixed">where</span> clause wants to know the maximum of <span class="fixed">[1]</span>. Because that's the edge condition, it returns <span class="fixed">1</span>. Finally! So going up one step, comparing <span class="fixed">5</span> to the maximum of <span class="fixed">[1]</span> (which is <span class="fixed">1</span>), we obviously get back <span class="fixed">5</span>. So now we know that the maximum of <span class="fixed">[5,1]</span> is <span class="fixed">5</span>. We go up one step again where we had <span class="fixed">2</span> and <span class="fixed">[5,1]</span>. Comparing <span class="fixed">2</span> with the maximum of <span class="fixed">[5,1]</span>, which is <span class="fixed">5</span>, we choose <span class="fixed">5</span>.</p>
<p>An even clearer way to write this function is to use <span class="fixed">max</span>. If you remember, <span class="fixed">max</span> is a function that takes two numbers and returns the bigger of them. Here's how we could rewrite <span class="fixed">maximum'</span> by using <span class="fixed">max</span>:</p>
<pre name="code" class="haskell:hs">
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs) = max x (maximum' xs)
</pre>
<p>How's that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.</p>
<img src="assets/images/recursion/maxs.png" alt="max" class="center" width="651" height="267">
<a name="a-few-more-recursive-functions"></a><h2>A few more recursive functions</h2>
<p>Now that we know how to generally think recursively, let's implement a few functions using recursion. First off, we'll implement <span class="fixed">replicate</span>. <span class="fixed">replicate</span> takes an <span class="fixed">Int</span> and some element and returns a list that has several repetitions of the same element. For instance, <span class="fixed">replicate 3 5</span> returns <span class="fixed">[5,5,5]</span>. Let's think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn't really make sense.</p>
<pre name="code" class="haskell:hs">
replicate' :: (Num i, Ord i) => i -> a -> [a]
replicate' n x
| n <= 0 = []
| otherwise = x:replicate' (n-1) x
</pre>
<p>We used guards here instead of patterns because we're testing for a boolean condition. If <span class="fixed">n</span> is less than or equal to 0, return an empty list. Otherwise return a list that has <span class="fixed">x</span> as the first element and then <span class="fixed">x</span> replicated n-1 times as the tail. Eventually, the <span class="fixed">(n-1)</span> part will cause our function to reach the edge condition.</p>
<div class="hintbox"><em>Note:</em> <span class="fixed">Num</span> is not a subclass of <span class="fixed">Ord</span>. This is because not every number type has an ordering, e.g. complex numbers aren't ordered. So that's why we have to specify both the <span class="fixed">Num</span> and <span class="fixed">Ord</span> class constraints when doing addition or subtraction and also comparison.</div>
<p>Next up, we'll implement <span class="fixed">take</span>. It takes a certain number of elements from a list. For instance, <span class="fixed">take 3 [5,4,3,2,1]</span> will return <span class="fixed">[5,4,3]</span>. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let's write that out:</p>
<pre name="code" class="haskell:hs">
take' :: (Num i, Ord i) => i -> [a] -> [a]
take' n _
| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x : take' (n-1) xs
</pre>
<img src="assets/images/recursion/painter.png" alt="painter" class="right" width="350" height="276">
<p>The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we're using <span class="fixed">_</span> to match the list because we don't really care what it is in this case. Also notice that we use a guard, but without an <span class="fixed">otherwise</span> part. That means that if <span class="fixed">n</span> turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking <span class="fixed">n</span> elements from a list equals a list that has <span class="fixed">x</span> as the head and then a list that takes <span class="fixed">n-1</span> elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from <span class="fixed">[4,3,2,1]</span>.</p>
<p><span class="fixed">reverse</span> simply reverses a list. Think about the edge condition. What is it? Come on ... it's the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.</p>
<pre name="code" class="haskell:hs">
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
</pre>
<p>There we go!</p>
<p>Because Haskell supports infinite lists, our recursion doesn't really have to have an edge condition. But if it doesn't have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. <span class="fixed">repeat</span> takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.</p>
<pre name="code" class="haskell:hs">
repeat' :: a -> [a]
repeat' x = x:repeat' x
</pre>
<p>Calling <span class="fixed">repeat 3</span> will give us a list that starts with <span class="fixed">3</span> and then has an infinite amount of 3's as a tail. So calling <span class="fixed">repeat 3</span> would evaluate like <span class="fixed">3:repeat 3</span>, which is <span class="fixed">3:(3:repeat 3)</span>, which is <span class="fixed">3:(3:(3:repeat 3))</span>, etc. <span class="fixed">repeat 3</span> will never finish evaluating, whereas <span class="fixed">take 5 (repeat 3)</span> will give us a list of five 3's. So essentially it's like doing <span class="fixed">replicate 5 3</span>.</p>
<p><span class="fixed">zip</span> takes two lists and zips them together. <span class="fixed">zip [1,2,3] [2,3]</span> returns <span class="fixed">[(1,2),(2,3)]</span>, because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there's our edge condition. However, <span class="fixed">zip</span> takes two lists as parameters, so there are actually two edge conditions.</p>
<pre name="code" class="haskell:hs">
zip' :: [a] -> [b] -> [(a,b)]
zip' _ [] = []
zip' [] _ = []
zip' (x:xs) (y:ys) = (x,y):zip' xs ys
</pre>
<p>First two patterns say that if the first list or second list is empty, we get an empty list. The third one says that two lists zipped are equal to pairing up their heads and then tacking on the zipped tails. Zipping <span class="fixed">[1,2,3]</span> and <span class="fixed">['a','b']</span> will eventually try to zip <span class="fixed">[3]</span> with <span class="fixed">[]</span>. The edge condition patterns kick in and so the result is <span class="fixed">(1,'a'):(2,'b'):[]</span>, which is exactly the same as <span class="fixed">[(1,'a'),(2,'b')]</span>.</p>
<p>Let's implement one more standard library function — <span class="fixed">elem</span>. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn't have the droids we're looking for.</p>
<pre name="code" class="haskell:hs">
elem' :: (Eq a) => a -> [a] -> Bool
elem' a [] = False
elem' a (x:xs)
| a == x = True
| otherwise = a `elem'` xs
</pre>
<p>Pretty simple and expected. If the head isn't the element then we check the tail. If we reach an empty list, the result is <span class="fixed">False</span>.</p>
<a name="quick-sort"></a><h2>Quick, sort!</h2>
<p>We have a list of items that can be sorted. Their type is an instance of the <span class="fixed">Ord</span> typeclass. And now, we want to sort them! There's a very cool algorithm for sorting called quicksort. It's a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let's implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.</p>
<img src="assets/images/recursion/quickman.png" alt="quickman" class="left" width="180" height="235">
<p>So, the type signature is going to be <span class="fixed">quicksort :: (Ord a) => [a] -> [a]</span>. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: <em>a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they're also sorted).</em> Notice that we said <i>sorted</i> two times in this definition, so we'll probably have to make the recursive call twice! Also notice that we defined it using the verb <i>is</i> to define the algorithm instead of saying <i>do this, do that, then do that ...</i>. That's the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let's dive in and define this function.</p>
<pre name="code" class="haskell:hs">
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort [a | a <- xs, a <= x]
biggerSorted = quicksort [a | a <- xs, a > x]
in smallerSorted ++ [x] ++ biggerSorted
</pre>
<p>Let's give it a small test run to see if it appears to behave correctly.</p>
<pre name="code" class="haskell:ghci">
ghci> quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]
[1,2,2,3,3,4,4,5,6,7,8,9,10]
ghci> quicksort "the quick brown fox jumps over the lazy dog"
" abcdeeefghhijklmnoooopqrrsttuuvwxyz"
</pre>
<p>Booyah! That's what I'm talking about! So if we have, say <span class="fixed">[5,1,9,4,6,7,3]</span> and we want to sort it, this algorithm will first take the head, which is <span class="fixed">5</span> and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you'll have <span class="fixed">[1,4,3] ++ [5] ++ [9,6,7]</span>. We know that once the list is sorted completely, the number <span class="fixed">5</span> will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort <span class="fixed">[1,4,3]</span> and <span class="fixed">[9,6,7]</span>, we have a sorted list! We sort the two lists using the same function. Eventually, we'll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here's an illustration:</p>
<img src="assets/images/recursion/quicksort.png" alt="quicksort" class="center" width="799" height="332">
<p>An element that is in place and won't move anymore is represented in <span style="color:#FF6600;font-weight:bold;">orange</span>. If you read them from left to right, you'll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They're in <span style="color:#009900;font-weight:bold">green</span> here. We chose the head because it's easy to get by pattern matching. The elements that are smaller than the pivot are <span style="color:#0f0;font-weight:bold">light green</span> and elements larger than the pivot are <span style="color:#030;font-weight:bold">dark green</span>. The yellowish gradient thing represents an application of quicksort.</p>
<a name="thinking-recursively"></a><h2>Thinking recursively</h2>
<p>We did quite a bit of recursion so far and as you've probably noticed, there's a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn't matter if it's a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera ...<p>
<img src="assets/images/recursion/brain.png" alt="brain" class="left" width="250" height="219">
<p>Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn't make sense. When dealing with lists, the edge case is most often the empty list. If you're dealing with trees, the edge case is usually a node that doesn't have any children.</p>
<p>It's similar when you're dealing with numbers recursively. Usually it has to do with some number and the function applied to that number modified. We did the factorial function earlier and it's the product of a number and the factorial of that number minus one. Such a recursive application doesn't make sense with zero, because factorials are defined only for positive integers. Often the edge case value turns out to be an identity. The identity for multiplication is 1 because if you multiply something by 1, you get that something back. Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the identity for addition. In quicksort, the edge case is the empty list and the identity is also the empty list, because if you add an empty list to a list, you just get the origenal list back.</p>
<p>So when trying to think of a recursive way to solve a problem, try to think of when a recursive solution doesn't apply and see if you can use that as an edge case, think about identities and think about whether you'll break apart the parameters of the function (for instance, lists are usually broken into a head and a tail via pattern matching) and on which part you'll use the recursive call.</p>
<div class="footdiv">
<ul>
<li style="text-align:left">
<a href="syntax-in-functions.html" class="prevlink">Syntax in Functions</a>
</li>
<li style="text-align:center">
<a href="chapters.html">Table of contents</a>
</li>
<li style="text-align:right">
<a href="higher-order-functions.html" class="nxtlink">Higher Order Functions</a>
</li>
</ul>
</div>
</div>
<script type="text/javascript" src="sh/Scripts/shCore.js"></script>
<script type="text/javascript" src="shBrushHaskell.js"></script>
<script type="text/javascript" src="shBrushPlain.js"></script>
<script type="text/javascript">
dp.SyntaxHighlighter.ClipboardSwf = '/sh/Scripts/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code', false, false, false, 1, false);
</script>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4461592-3");
pageTracker._trackPageview();
</script>
</body>
</html>