Differences
This shows you the differences between two versions of the page.
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 ===== | ||