I am learning Haskell and I decided to embark on a small sudoku solver as a project. I have been using this assignment as a guide and I recently hit a wall on sub-problem D2
which is to create a function that will generate the sudoku blocks (9 3x3 grids) from a Sudoku board (a 9x9 grid).
I started writing the following code but I quickly realized it was a terrible idea. It is clunky code that is not idiomatic Haskell (in my opinion) and completely violates the DRY principle.
type Block = [Maybe Int]
data Sudoku = Sudoku [[Maybe Int]]
blocks :: Sudoku -> [Block]
blocks (Sudoku rs) = block1 : block2 : block3 : block4 : block5 : block6 : block7 : block8 : block9 : []
where block1 = [(rs!!0)!!0] ++ [(rs!!0)!!1] ++ [(rs!!0)!!2] ++ [(rs!!1)!!0] ++ [(rs!!1)!!1] ++ [(rs!!1)!!2]++ [(rs!!2)!!0] ++ [(rs!!2)!!1] ++ [(rs!!2)!!2]
block2 = ...
block3 = ...
...
I was wondering how to write a more concise and idiomatic function to complete the task? How would you implement it? Any ideas are appreciated!
I have also consulted this previous and possibly related SO question but I was not sure how to convert the pythonic solution to Haskell and whether or not I even should. I have also seen this question but I have structured my Sudoku board differently.
All of my current code can be found here. Also, let me know if I can clarify anything.
First, write a groupBy3
function which groups a list by three elements:
groupBy3 :: [a] -> [[a]]
Then use the following chain of operations:
map groupBy3
transpose
concat
groupBy3
map concat
Written on my phone, so it's untested but it should be close.
Update: I verified that this works:
groupBy3 (a:b:c:ds) = [a,b,c] : groupBy3 ds
groupBy3 [] = []
groupBy3 as = [ as ] -- won't happen
boxes = map concat . groupBy3 . concat . transpose . map groupBy3
grid = [ [ [i,j] | j <- ['1'..'9'] ] | i <- ['a'..'i'] ]
test1 = boxes grid