找到 9 个 3x3 "blocks" 个数独板 (Haskell)

Find 9 3x3 "blocks" of Sudoku board (Haskell)

我正在学习 Haskell,我决定着手开发一个小型数独求解器作为一个项目。我一直在使用 this assignment 作为指南,最近我在子问题 D2 上遇到了困难,该子问题是创建一个函数,该函数将从数独板(a 9x9 网格)。

我开始编写以下代码,但我很快意识到这是一个糟糕的想法。这是笨拙的代码,不符合惯用 Haskell(在我看来)并且完全违反了 DRY 原则。

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 = ...
        ...

我想知道如何编写一个更简洁和地道的函数来完成任务?你会如何实现它?任何想法表示赞赏!

我也咨询了 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 问题,但我的数独板结构有所不同。

可以找到我当前的所有代码here。另外,如果我能澄清任何事情,请告诉我。

首先,编写一个 groupBy3 函数,将列表按三个元素分组:

groupBy3 :: [a] -> [[a]]

然后使用以下操作链:

  • map groupBy3
  • transpose
  • concat
  • groupBy3
  • map concat

写在我的 phone 上,因此未经测试,但应该接近。

更新:我证实这有效:

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

不是这个问题的确切答案,但使用数组比使用列表要好得多。由于您需要更改元素,您可能需要使用 mutable arrays.

然后您可以将块实现为基本数组的 "views",只需对索引操作应用不同的偏移量即可,无需复制它们。

另一种方法,虽然可能不如公认的答案那么优雅:

map concat [(map  (take 3 . drop i) . (take 3 . drop j)) grid | i <- [0, 3, 6], j <- [0, 3, 6]]

其中 grid 是 9x9 数独网格。