diff --git a/VL2.tex b/VL2.tex index 591ef68..12f8b69 100644 --- a/VL2.tex +++ b/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} \ No newline at end of file