Parsing 2d arrays from text?

Conventional wisdom crudely expressed: never use Prelude.foldl; use foldl' with a strict accumulator, otherwise foldr.

There are two questions, I think, one his how to parse the incoming String or ByteString or whatever; the other is what sort of structure to read it into. Of course these aren't completely independent, but I wonder if you couldn't separate concerns a bit more. As I guess you would know, a really barbaric Prelude way of getting a String into the right shape might be like so:

parseAsLists :: String -> ((Int,Int), [[Int]])
parseAsLists = start . lines where
  start (x:xs) = (size (words x), rows xs)        
  size (x:y:_) = (read x, read y) :: (Int,Int)   
  rows ls = map row ls
  row l  = map read (words l)

  main = do str <- getContents
            print $ parseAsLists str

 -- $ cat linep.txt | runhaskell lineparser.hs
--  ((2,3),[[1,2,3],[4,5,6]])

We divide the string into lines; the first line (assumed to exist!) is divided into numerals, the first two (assumed to exist!) are read (assumed readable!) and returned as a pair; then similarly we make lists of Ints by reading the words of the succeeding lines.

This is barbaric in a number of ways: it just fails on bad strings; it uses String instead of Text or ByteString; it parses into Haskell lists which are good for some purposes bad for others. The other stringy types have a similar interface, so things would go similarly.

But I think the main trouble was finding a data type to read into? Here I wonder if you might not find the (builtin) array package natural:

https://hackage.haskell.org/package/array-0.5.0.0/docs/Data-Array.html

Since you are making a finite array of Int, an unboxed type, you might do well to use the strict, unboxed kind of arrays. The different types have the same interface. For one dimensional arrays, the vector package is more convenient.

https://hackage.haskell.org/package/array-0.5.0.0/docs/Data-Array-Unboxed.html

We can recycle our crude parseAsLists as a start:

import Data.Array.Unboxed

parseArraySimple :: String -> UArray (Int,Int) Int
parseArraySimple str = listArray ((0,0),(x',y')) arr
 where
   ((x,y),arrs) = parseAsLists str
   (x',y') = (x-1,y-1)  -- to use them as indices
   arr  = concat arrs 

Then we could write, say:

main = do str <- readFile "arr.txt"
          let arr = parseArraySimple str
              loop = do pt <- readLn :: IO (Int,Int)
                        print $ arr ! pt
                        loop
          loop 

With the following highly error prone result:

   -- $ ghc -O2 lineparser2.hs 
   -- $ ./lineparser2
   -- Index?
   -- (0,0)
   -- 1
   -- Index?
   -- (1,2)
   -- 6
   -- Index?
   -- (20,20)
   -- lineparser2.hs: Error in array index
/r/haskell Thread