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}).
|
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}
|
\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}
|
\end{document}
|
Loading…
Reference in New Issue
Block a user