Mark Needham

Thoughts on Software Development

Haskell: Print friendly representation of an Array

with 8 comments

Quite frequently I play around with 2D arrays in Haskell but I’ve never quite worked out how to print them in a way that makes it easy to see the contents.

I’m using the array from the ‘Data.Array’ module because it seems to be easier to transform them into a new representation if I want to change a value in one of the cells.

The function to create one therefore looks like this:

import Data.Array
 
grid :: Int -> a -> Array(Int, Int) a
grid size value = array ((0,0),(size-1,size-1)) [((x,y),value) | x<-[0..size-1], y<-[0..size-1]]

Which we can use like this:

> grid 2 0
array ((0,0),(1,1)) [((0,0),0),((0,1),0),((1,0),0),((1,1),0)]

I wanted to get the output to read like this:

0 0
0 0

I initially tried to override the ‘Show’ implementation but wasn’t very successful in trying to do that – I’m not sure whether that’s actually possible but someone on the IRC channel suggested I should probably try and write my own function to print it out.

I ended up with the following:

printGrid :: Show a => Array (Int, Int) a -> IO [()]
printGrid grid = sequence $ map (putStrLn . textRepresentation) $ toSimpleArray grid
 
toSimpleArray :: Array (Int, Int) a -> [[a]]	
toSimpleArray grid = [[grid ! (x, y) | x<-[lowx..highx]] |  y<-[lowy..highy]] 
	where ((lowx, lowy), (highx, highy)) =  bounds grid
 
textRepresentation :: Show a => [a] -> String
textRepresentation row = foldl (\acc y -> acc ++ (show y) ++ " ") "" row

The toSimpleArray function converts the array back into a format which is easier to deal with. So for a simple array:

> toSimpleArray (grid 2 0)
[[0,0],[0,0]]

We then map over the new array and apply textRepresentation over each row to get a text representation.

The textRepresentation function works like this:

> textRepresentation [1, 2, 3]
"1 2 3 "

After that we map putStrLn over the result which gives us a collection of IO monads.

Unfortunately that still doesn’t print the array out so we need sequence which I came across in the Learn You A Haskell tutorial:

sequence takes a list of I/O actions and returns an I/O actions that will perform those actions one after the other. The result contained in that I/O action will be a list of the results of all the I/O actions that were performed.

And eventually this is how we use printGrid:

> printGrid $ grid 2 0
0 0 
0 0 
[(),()]

There must be ways to simplify some of that code so if you can see any let me know in the comments!

Written by Mark Needham

April 3rd, 2012 at 9:52 pm

Posted in Haskell

Tagged with

  • Bill Six

    “I initially tried to override the ‘Show’ implementation but wasn’t very
    successful in trying to do that”

    I don’t believe that is possible in Haskell.

    The only thing I might suggest is to use mapM instead of using map and sequence.

    :t mapM
    => mapM :: Monad m => (a -> m b) -> [a] -> m [b]

    printGridMapM :: Show a => Array (Int, Int) a -> IO [()]
    printGridMapM = mapM (putStrLn . textRepresentation) . toSimpleArray

  • Bill Six

    however, why am I using the IO Monad?  I’m only using mapM because of putStrLn.  If I removed putStrLn, mapM could be replaced with map. 

    printGridMap :: Show a => Array (Int, Int) a -> [String]
    printGridMap = map textRepresentation . toSimpleArray

  • http://www.markhneedham.com/blog Mark Needham

    @90f89c7767c128ce79b41fb46ef5f422:disqus  Only problem with this version is it doesn’t output like the original one:

    > printGrid $ toComplexArray [[White, White, White], [Blue, White, Blue], [Blue, Blue, Blue]]

    ["W W W ","Bl W Bl ","Bl Bl Bl "]

    The ‘mapM’ tip is very cool though, I’ve put that in!

  • David Turner

    I would write

    textRepresentation =  (++ ” “) . intercalate ” ” . map show

    and you probably don’t really want the (++ ” “) – that just adds a trailing space. (The intercalate function is in Data.List).

    You may also want to use mapM_ (or sequence_) which is the same as mapM (respectively sequence) except it discards the results and returns () so doesn’t show anything when you run it in GHCi.

  • David Turner

    Also, ‘mapM putStrLn’ is the same as ‘putStrLn . unlines’ where unlines is also in Data.List. That keeps you out of the IO monad for longer.

  • http://www.markhneedham.com/blog Mark Needham

    @8ba6e5de7bc28c280457a075f599677d:disqus 

    Ah neat, so I end up with this:textRepresentation :: Show a => [a] -> StringtextRepresentation =  intercalate ” ” . map show

    I’m gathering that it’s idiomatic haskell to define the function signature and then use point free notation rather than naming parameters in the function definition, is that right?

  • David Turner

    Not convinced with 100% pointfreedom myself. You can transform any expression into pointfree form (there’s a cabal package, pointfree, to do this automatically) but I think it’s better to take a view on whether the pointfree version is more or less readable than the pointful one.

    In this case, writing

    textRepresentation row = intercalate ” ” $ map show row

    is fine too, but the fact that ‘row’ appears at the end of each expression indicates that it’s redundant. I often write the pointful version first and then refactor it, perhaps until it’s pointfree but often not. I find the pointful versions easier to manipulate much of the time.

    For example, it’s definitely preferable to me to write

    swap (x, y) = (y, x)

    over

    swap = uncurry $ flip (,)

    even though these are equivalent. (The function swap is defined in Data.Tuple, so you’d never write either of these, but you hopefully get the idea.)

  • http://www.markhneedham.com/blog Mark Needham

    @8ba6e5de7bc28c280457a075f599677d:disqus yeh that makes sense. Thanks for giving me tips on how to improve the code every time I write about something by the way. It’s very helpful