As I mentioned in a previous post, I’m trying to learn Haskell. I’m not progressing super quickly because it’s not a terribly high priority, but it’s sort of fun to think about programming problems in a totally different way. While Haskell (being a functional language) definitely fits this niche, the book that I’m following does not include any exercises — you can sort of work the examples ahead of time and compare your solution to the book’s, but that’s a poor substitute.
I was helping a friend of mine (a sophomore in computer science) with an assignment he was working on recently, when it struck me that the straightforward, simple assignments he’s doing might make good projects to tackle in Haskell. The course he’s in is a data structures course, so a lot of the projects have fairly simple input / output specifications, and somewhat complex backend requirements. I chose to ignore the data structure parts (I’ll save implementing stacks / linked lists / etc. in Haskell for another day) and tried to tackle just the functionality. The first (and so far, only) project I worked on reads in two matrices and either multiplies them or informs the user that the matrices’ sizes are incompatible. The multiplication is straightforward — I’m not using the Strassen algorithm — and the input / output formats are easy as well.
The project turned out to be both more difficult and more instructive than I had planned. Some observations I made while writing my program:
- Parsing in a functional language is totally foreign to me. The math wasn’t all that hard to grasp (though it wasn’t exactly straightforward either) but doing simple things like splitting a line into chunks (“tokens”) based on some delimiter is a completely new experience in a functional language.
- Nearly all my methods have essentially one line of functionality. I’m not sure if this is how Haskell is “supposed” to be written, or if it shows that I’m doing something wrong. Either way, the length of functions is something I’m definitely going to pay more attention to when I look at examples in the future.
- I’m not very good at list comprehensions. Below are two ways of multiplying matrices: First, my way:
12345678910111213matrixMult :: Num a => [[a]] -> [[a]] -> [[a]]matrixMult x y = matrixMultT x $ transpose ymatrixMultT :: Num a => [[a]] -> [[a]] -> [[a]]matrixMultT  _ = matrixMultT (a:as) b = calcRow a b : matrixMultT as bcalcRow :: Num a => [a] -> [[a]] -> [a]calcRow _  = calcRow a (b:bs) = calcCell a b : calcRow a bscalcCell :: Num a => [a] -> [a] -> acalcCell col row = foldl1 (+) $ zipWith (*) col row
Second, the way it’s implemented on rosettacode.org:
12mmult :: Num a => [[a]] -> [[a]] -> [[a]]mmult a b = [[ sum $ zipWith (*) ar bc | bc <- (transpose b)] | ar <- a ]
All in all, I learned a lot. It’s some pretty ugly code, and I may rewrite it further, or tackle a different project from the same class and try to go about it in a better way. If you’d like to look at my code or compile / run it (with the understanding this is just sort of doodling-type-code and isn’t documented or optimized or very good) feel free to download it.