Add filter, fold and summary slides for "Recursion patterns" section
This commit is contained in:
parent
729c460112
commit
cd8f11e259
154
VL2.tex
154
VL2.tex
@ -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}
|
Loading…
Reference in New Issue
Block a user