Joe
Joe

Reputation: 365

Printing a matrix with Haskell

I need to print out a matrix in haskell so it looks like this:

main> putStr (showMat [[1,-500,-4], [100,15043,6], [5,3,10]])
       1  -500 -4
     100 15043  6
       5     3 10

So far I have come up with this:

type Matrix a = [[a]]
type IntMat = Matrix Integer

showMat :: IntMat -> String
showMat [] = ""
showMat ((y:ys):xs)  = (printRow (rowmaxs) (elements) (y:ys)) ++ "\n" ++ showMat xs
  where rowmaxs = rowMaxs ((y:ys):xs) ; elements = elementLengths (y:ys)

rowMaxs :: IntMat -> [Int]
rowMaxs [] = []
rowMaxs (x:xs) = [length (show (maximum (x)))] ++ (rowMaxs xs)

elementLengths :: [Integer] -> [Int]
elementLengths [] = []
elementLengths (y:ys) = [length (show y)] ++ (elementLengths ys)

printRow :: [Int] -> [Int] -> [Integer] -> String
printRow [] (a:as) (y:ys) = ""
printRow (z:zs) (a:as) [] = ""
printRow [] [] (y:ys)     = ""
printRow [] [] []         = ""
printRow (z:zs) (a:as) (y:ys) = addSpaces (z-a) ++ show y ++ [' '] ++ printRow zs as ys

addSpaces :: Int -> String
addSpaces 0 = ""
addSpaces n = " " ++ addSpaces (n-1)

Which returns this:

Main> putStr (showMat [[1,23,1],[23,56,789],[1234,0,1]])
      1  23    1 
      23   56 
     1234

I can see the loss of the elements at the end of rows is due to the cases in the printRow function, but I don't know how to fix it. Also the characters don't take into account the ones printed before them. Any help on how I can fix this would be appreciated.

Upvotes: 1

Views: 4264

Answers (2)

David
David

Reputation: 8395

I've written a small script some time ago that takes a tab-separated table (CSV style) and prettyprints it to the console. I've extracted the relevant parts and uploaded the source code here. For example, given your input

m = [[1,23,456],[78,-90,123],[4567,8,9]]

putStrLn $ showMat m will make it into

  1  -500 -4
100 15043  6
  5     3 10

There's also a function (commented out) at the very bottom if you want left alignment.

Upvotes: 3

dave4420
dave4420

Reputation: 47052

In showMat you calculate rowmaxs and elements anew for each row. In particular, rowmaxs has an element for each row left in the matrix, but printRow uses it as meaning something for each column of the matrix.

Edit: Here's a slightly better version of showMat:

showMat :: IntMat -> String
showMat rows = concat (map perRow rows)
  where rowmaxs = rowMaxs rows
        perRow cols = printRow rowmaxs elements cols ++ "\n"
          where elements = elementLengths cols

It's still buggy --- rowMaxs (tries to) calculate the maximum length of the numbers in each row, but you really want the maximum length of the numbers in each column. One consequence of that is that you occasionally pass negative numbers to addSpaces, which doesn't cope terribly well, so here's a version of addSpaces that behaves more gracefully:

addSpaces :: Int -> String
addSpaces n = replicate n ' '

So now we get this:

*Main> putStr (showMat [[1,23,1],[23,56,789],[1234,0,1]])
 1  23    1 
23  56  789 
1234   0    1 

Better, but not yet working properly.

I have not fixed all the bugs in your code because I sense you are still learning and need the experience of finding them for yourself.

I advise using map/zipWith/zipWith3 instead of explicitly writing the recursion, as it makes code easier to understand.

Upvotes: 1

Related Questions