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!

Be Sociable, Share!

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