Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
pp:other-exercises [2021/03/13 14:57]
pdmatei
pp:other-exercises [2021/03/21 08:14] (current)
pdmatei
Line 1: Line 1:
 +===== Matrices ======
 +
 +==== Haskell implementation of foldl ====
 +
 +In Haskell, ''​foldl''​ is implemented as follows:
 +<​code>​
 +foldl op acc []     = acc                  ​
 +foldl op acc (h:t) = foldl op (op acc h) t
 +</​code>​
 +
 +Note the call of the ''​op''​ function.
 +
 +==== An exercise in modular programming with higher-order functions ====
 +
 +Let us consider the task of matrix multiplication in Haskell. Example:
 +<​code>​
 +  1   ​2 ​  ​3 ​           1   ​0 ​  ​0 ​        ​1+3 ​ 2  2+3      4   ​2 ​  5
 +  4   ​5 ​  ​6 ​     x     ​0 ​  ​1 ​  ​1 ​    ​= ​  ​4+6 ​ 5  5+6  =   ​10 ​ 5   11
 +  7   ​8 ​  ​9 ​           1   ​0 ​  ​1 ​        ​7+9 ​ 8  8+9      16  8   17
 +</​code>​
 +
 +
 +=== Matrix representation in Haskell ===
 +
 +The basic representation building-block in Haskell is the list. We can represent matrices in Haskell as a list where each element is a row (hence a list of elements). Example:
 +
 +<code haskell>
 +m = [[1,​2,​3],​[4,​5,​6],​[7,​8,​9]]
 +</​code>​
 +
 +A good warmup exercise is to write a nice display function for matrices. We transform each element of a line into a string:
 +
 +<code haskell>
 +displayline l = map (\e->​(show e)++" ") l
 +</​code>​
 +
 +The code can be improved for legibility:
 +<code haskell>
 +displayline = map ((++" "​).show) ​
 +</​code>​
 +
 +
 +Next, we fold the list into a string with a newline character: ​
 +
 +<code haskell>
 +displayline l = foldr (++) "​\n"​ (map ((++" "​).show) l)
 +</​code>​
 +
 +and simplify again, by expressing the pipeline function calls as functional composition:​
 +<code haskell>
 +displayline :: Show a => [a] -> [Char]
 +displayline = (foldr (++) "​\n"​) .  (map ( (++ " ") . show ) )
 +</​code>​
 +
 +Note that we need to explicitly state the type of display. Sometimes, in Haskell, an explicit type declaration is required. For now, we omit details.
 +
 +Next, we apply the above process on all matrix lines:
 +
 +<code haskell>
 +display :: Show a => [[a]] -> [[Char]]
 +display = 
 +    let bind = foldr (++) "​\n"​
 +    in bind.(map (bind . (map ((++" ") . show ) ) ) )
 +</​code>​
 +
 +Notice that we have separated the binding process, because we reuse it.
 +
 +Finally, we make all matrices displayable:​
 +<code haskell>
 +instance (Show a) => Show [[a]] where
 +    show = 
 +        let bind = foldr (++) "​\n"​
 +        in bind.(map (bind.(map ((++" "​).show ) ) ) )
 +</​code>​
 +More details about this implementation (e.g. instances, classes) will be given in future lectures.
 +        ​
 +=== Matrix multiplication ===
 +
 +== Step 1: Transposition ==
 +
 +Matrix multiplication operates on the **lines** of the first matrix and **columns** of the second. We transpose the second matrix, so that we now operate on lines on both matrices. The following code extracts **the first line** from a matrix ''​m'':​
 +<code haskell>
 +map head m
 +</​code>​
 +A matrix ''​m''​ **without** its first column is:
 +<code haskell>
 +map tail m
 +</​code>​
 +Finally transposition is given by:
 +<code haskell>
 +transpose ([]:_) = []
 +transpose m = (map head m) : transpose (map tail m)
 +</​code>​
 +Notice that the //basis case// corresponds to a **list containing empty lists**.
 +
 +== Step 2: Computing multiplication ==
 +
 +To compute the ''​i,​j''​th **element** of the multiplication matrix, we need to multiply per element the ''​i''​th line by the ''​j''​th column:
 +<code haskell>
 +zipWith (*) li cj
 +</​code>​
 +and then add-up the values:
 +<code haskell>
 +foldr (+) 0 (zipWith (*) li cj)
 +</​code>​
 +
 +To obtain the ''​i''​th **line** of the multiplication matrix, we need to repeat the above process **for each column of the second matrix**, in other words, for each line of its transposition:​
 +
 +<code haskell>
 +map (\col -> foldr (+) 0 (zipWith (*) li col) ) (transpose m2)
 +</​code>​
 +
 +Finally, to obtain the multiplication matrix, we need to compute all its lines, hence:
 +<code haskell>
 +mult m1 m2 = 
 +  map (\line -> map (\col -> foldr (+) 0 (zipWith (*) line col) ) (transpose m2) ) m1
 +</​code>​
 +
 +=== Matrices as images ===
 +
 +A matrix can be used to represent a **rasterized image** (a collection of pixels). In this example, we consider that pixels can have values: ' ' (white), '​.'​ (grey) and '​*'​ (black).
 +
 +Higher-order functions can be naturally used to represent image-transformations,​ for instance, flipping:
 +
 +<code haskell>
 +flipH = map reverse
 +flipV = reverse
 +</​code>​
 +
 +Rotations:
 +<code haskell>
 +rotate90left = flipV.transpose
 +rotate90right = flipH.transpose
 +</​code>​
 +
 +The //​negative//​ of an image:
 +<code haskell>
 +invert = map (map (\x->if x=='​*'​ then ' ' else '​*'​) )
 +</​code>​
 +
 +Scaling an image horizontally:​
 +<code haskell>
 +scalex = foldr (\h t->​h:​h:​t) []    ​
 +</​code>​
 +
 +Scaling vertically:
 +<code haskell>
 +scaley = map scalex
 +</​code>​
 +
 +Balanced scale:
 +<code haskell>
 +scale = scalex . scaley
 +</​code>​
 +
 +Or just a random sequence of operations:
 +<code haskell>
 +rand = foldr (.) id [rotate90left,​ invert, scale]
 +</​code>​
 +
 ===== Function composition and function application ===== ===== Function composition and function application =====