Haskell implementation of foldl

In Haskell, foldl is implemented as follows:

foldl op acc []     = acc                  
foldl op acc (h:t) = foldl op (op acc h) t

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:

  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

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:

m = [[1,2,3],[4,5,6],[7,8,9]]

A good warmup exercise is to write a nice display function for matrices. We transform each element of a line into a string:

displayline l = map (\e->(show e)++" ") l

The code can be improved for legibility:

displayline = map ((++" ").show) 

Next, we fold the list into a string with a newline character:

displayline l = foldr (++) "\n" (map ((++" ").show) l)

and simplify again, by expressing the pipeline function calls as functional composition:

displayline :: Show a => [a] -> [Char]
displayline = (foldr (++) "\n") .  (map ( (++ " ") . show ) )

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:

display :: Show a => [[a]] -> [[Char]]
display = 
    let bind = foldr (++) "\n"
    in bind.(map (bind . (map ((++" ") . show ) ) ) )

Notice that we have separated the binding process, because we reuse it.

Finally, we make all matrices displayable:

instance (Show a) => Show [[a]] where
    show = 
        let bind = foldr (++) "\n"
        in bind.(map (bind.(map ((++" ").show ) ) ) )

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:

map head m

A matrix m without its first column is:

map tail m

Finally transposition is given by:

transpose ([]:_) = []
transpose m = (map head m) : transpose (map tail m)

Notice that the basis case corresponds to a list containing empty lists.

Step 2: Computing multiplication

To compute the i,jth element of the multiplication matrix, we need to multiply per element the ith line by the jth column:

zipWith (*) li cj

and then add-up the values:

foldr (+) 0 (zipWith (*) li cj)

To obtain the ith 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:

map (\col -> foldr (+) 0 (zipWith (*) li col) ) (transpose m2)

Finally, to obtain the multiplication matrix, we need to compute all its lines, hence:

mult m1 m2 = 
  map (\line -> map (\col -> foldr (+) 0 (zipWith (*) line col) ) (transpose m2) ) m1

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:

flipH = map reverse
flipV = reverse

Rotations:

rotate90left = flipV.transpose
rotate90right = flipH.transpose

The negative of an image:

invert = map (map (\x->if x=='*' then ' ' else '*') )

Scaling an image horizontally:

scalex = foldr (\h t->h:h:t) []    

Scaling vertically:

scaley = map scalex

Balanced scale:

scale = scalex . scaley

Or just a random sequence of operations:

rand = foldr (.) id [rotate90left, invert, scale]

29. Sometimes it really helps to define function via function composition. For instance:

f x = (g.h) x
	where g x = 2*x
	      h x = x + 1

What type does f have? What type does g have?

What does the following function do?

f x = ff x
	where g x = 2*x
	      h x = x + 1
	      ff = f.h

What does the following function do?

f x = ff x
	where g x = 2*x
	      h x = x + 1
	      ff = h.f

30. Rewrite exercise 28 via function composition:

f l = 

34. Sometimes it helps to use functions in infix form. For instance, instead of mod x 2, we can write x mod 2. We can also define infix functions:

 x 'f' y = x + y

Implement a function for transforming lists into pairs, in exercise 32. and use it in the infix form.

35. Just in the same way, we can treat infix functions as prefix. We do this using round parentheses. Test:

		:t (+)
		:t (&&)
		:t (++)

36. What about function composition? Is it a special operator, or is it just a function as well? What does :t (.) do?

37. What does the function below do? Take parts of the function below, and test them.

f l = (((++) "?").((:) '>')) l

How about?

f = (((:) '>').(++"?"))

38. Write a function of a single line, such that:

f "Matei" = "{Matei}"

39. Use only (:), reverse and functional composition . to solve exercise 38

40. What does the function ($) to?

  (Use :t, and make up some tests)

41. Write the following implementation using only ($):

f "||" "Matei" = "||Matei||"

42. Solve exercise 13. from Lab 2 using $ (++ and reverse are also permitted) and other improvements (treat the case when the list has fewer elements):