Add filter, fold and summary slides for "Recursion patterns" section

This commit is contained in:
Julian Ospald 2015-04-19 19:51:14 +02:00
parent 729c460112
commit cd8f11e259
No known key found for this signature in database
GPG Key ID: 220CD1C5BDEED020
1 changed files with 154 additions and 0 deletions

154
VL2.tex
View File

@ -501,4 +501,158 @@ absList = map abs
Cool, right? So now we have abstracted out the \textbf{recursion pattern} that is all the same for those 3 functions. \code{map} is actually part of the standard library (called \emph{Prelude}).
\end{frame}
\subsection{6.2. filter}
\begin{frame}[fragile]
\frametitle{6.2. filter}
Imagine we want to filter all even numbers of a list and throw away all others. I'll give you the type signature:
\begin{haskellcode}
filterEven :: [Int] -> [Int]
\end{haskellcode}
Solution?
\pause
\begin{haskellcode}
filterEven :: [Int] -> [Int]
filterEven [] = []
filterEven (x:xs)
| even x = x : filterEven xs
| otherwise = filterEven xs
\end{haskellcode}
\pause
Or: filter out all 0's, so we can count them later:
\begin{haskellcode}
filterZero :: [Int] -> [Int]
filterZero [] = []
filterZero (x:xs)
| x == 0 = x : filterZero xs
| otherwise = filterZero xs
\end{haskellcode}
Again: do you notice something?
\end{frame}
\begin{frame}[fragile]
\frametitle{6.2. filter (ctn.)}
Let's abstract out the common pieces! This will be our type signature:
\begin{haskellcode}
filter :: (a -> Bool) -> [a] -> [a]
\end{haskellcode}
Solution?
\pause
\begin{haskellcode}
filter :: (a -> Bool) -> [a] -> [a]
filter f [] = []
filter f (x:xs)
| f x = x : filter f xs
| otherwise = filter f xs
\end{haskellcode}
Again: this function is part of the \emph{Prelude} as well.
\end{frame}
\subsection{6.3. fold}
\begin{frame}[fragile]
\frametitle{6.3. fold}
There's one more important recursion pattern. Imagine you want the sum of all numbers of a list, so the function type signature would be:
\begin{haskellcode}
sum :: [Int] -> Int
\end{haskellcode}
Solution?
\pause
\begin{haskellcode}
sum :: [Int] -> Int
sum [] = 0
sum (x:xs) = x + sum xs
\end{haskellcode}
\pause
Or the product:
\begin{haskellcode}
prod :: [Int] -> Int
prod [] = 1
prod (x:xs) = x * prod xs
\end{haskellcode}
\pause
Or the length:
\begin{haskellcode}
length :: [a] -> Int
length [] = 0
length (x:xs) = 1 + length xs
\end{haskellcode}
\end{frame}
\begin{frame}[fragile]
\frametitle{6.3. fold (ctn.)}
To cut the story short, the abstract solution looks like this:
\begin{haskellcode}
fold :: b -> (a -> b -> b) -> [a] -> b
fold z f [] = z
fold z f (x:xs) = f x (fold z f xs)
\end{haskellcode}
Whoa! What's going on here?\\
Let's see...
\begin{itemize}[<+->]
\item \code{z} is what we return if the list is empty
\item \code{f} is our function (e.g. \code{(*)} or \code{(+)})
\item and the last remaining argument is the actual list we are working on
\end{itemize}
\onslide<+->
The function application has the following form:\\
\code{fold f z [a,b,c] == a `f` (b `f` (c `f` z))}
\vspace{\baselineskip}
\\
This folds from the right, so the \emph{Prelude} already defines a function which is very similar to ours and called \textbf{foldr}.
\end{frame}
\begin{frame}[fragile]
\frametitle{6.3. fold (ctn.)}
So how do our \code{sum}, \code{prod} and \code{length} functions look like if we use our \code{fold} abstraction?
\pause
\begin{haskellcode}
sum :: [Int] -> Int
sum xs = fold 0 (\x y -> x + y) xs
-- a Haskeller would write
sum = fold 0 (+)
prod :: [Int] -> Int
prod xs = fold 1 (\x y -> x * y) xs
length :: [a] -> Int
length xs = fold 0 (\x y -> 1 + y) xs
\end{haskellcode}
\end{frame}
\begin{frame}[fragile]
\frametitle{6.3. fold (ctn.)}
There is also a function that folds from the \emph{left} which is also in the \emph{Prelude} and called \textbf{foldl}.\\
To summarize:
\begin{haskellcode}
foldr f z [a,b,c] == a `f` (b `f` (c `f` z))
foldl f z [a,b,c] == ((z `f` a) `f` b) `f` c
\end{haskellcode}
For \code{foldl} the \code{z} is sort of the starting value.
\vspace{\baselineskip}
\\
\pause
We can even express foldl in terms of foldr and vice versa. If you are interested, have a look here: \url{http://lambda.jstolarek.com/2012/07/expressing-foldl-in-terms-of-foldr/}
\vspace{\baselineskip}
\\
You should definitely look them up in the Prelude and play with them: \url{https://hackage.haskell.org/package/base-4.8.0.0/docs/Prelude.html}
\vspace{\baselineskip}
\\
GHCi...
\begin{haskellcode}
> foldr (-) 0 [1, 2, 3]
> foldl (-) 0 [1, 2, 3]
\end{haskellcode}
\end{frame}
\begin{frame}[fragile]
\frametitle{6.3. summary}
\begin{itemize}[<+->]
\item if you find recurring patterns in your code, abstract them out! Experienced Haskellers avoid explicit recursion, unless the recursion pattern is so complex/specific that an abstraction doesn't make sense.
\item map, filter, fold etc are all dependent on the data type (here: lists). For new data types (e.g. a tree) you can and will write your own recursion abstractions
\item although these functions are so fundamental that they are already implemented for most data types out there
\end{itemize}
\end{frame}
\end{document}