Differences

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

Link to this comparison view

Both sides previous revision Previous revision
pp:high [2021/03/21 08:11]
pdmatei [Haskell implementation of foldl]
pp:high [2021/03/21 08:14] (current)
pdmatei
Line 1: Line 1:
- 
 ===== Higher-order functions ===== ===== Higher-order functions =====
  
Line 89: Line 88:
 A natural mistake is to consider ''​((:​).f)''​ as invalid, since it receives two parameters, and we cannot compose such a function with one receiving only one parameter. However, every function in Haskell receives one parameter and returns **a value** (which can be a function or a primitive value). A natural mistake is to consider ''​((:​).f)''​ as invalid, since it receives two parameters, and we cannot compose such a function with one receiving only one parameter. However, every function in Haskell receives one parameter and returns **a value** (which can be a function or a primitive value).
  
-There are other examples of useful higher-order functions. We illustrate them using examples:+There are other examples of useful higher-order functions. We illustrate them using examples, which you should test out:
 <​code>​ <​code>​
 filter (>3) [1,​2,​3,​4,​5,​2] filter (>3) [1,​2,​3,​4,​5,​2]
 zipWith (+) [1,2,3] [3,2,1] zipWith (+) [1,2,3] [3,2,1]
 </​code>​ </​code>​
- 
 Guess what each function does, by testing it on several lists. Guess what each function does, by testing it on several lists.
  
-==== Haskell implementation of foldl ====+===== Practice =====
  
-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>​