Haskell:我如何编写一个递归函数,它接受一个元素并给出它的列表,但是具有不同的数据类型?

iTw*_*nty 3 recursion haskell types

假设我有如下数据类型:

 data Cell = Cell (Maybe Player)
 data Board = Board [[Cell]]
Run Code Online (Sandbox Code Playgroud)

现在我想生成一个这样的递归函数:

 genBoard :: [Cell] -> Board
 genBoard [] = []
 genBoard c = (take 3 c) : (genBoard $ drop 3 c) -- takes list of 9 Cells and gives 3x3 list of cells
Run Code Online (Sandbox Code Playgroud)

显然上面的代码失败了,因为(:)不能将[Cell]添加到Board,尽管从技术上讲,Board只不过是[[Cell]].我需要将Board作为单独的数据类型来为它提供我自己的show函数.

到目前为止,我提出的最好的是:

genBoardList :: [Cell] -> [[Cell]]
genBoardList [] = []
genBoardList c =  (take 3 c) : (genBoardList $ drop 3 c)

boardListToBoard :: [[Cell]] -> Board
boardListToBoard [] = Board []
boardListToBoard s = Board s

genBoard :: [Cell] -> Board
genBoard = boardListToBoard . genBoardList
Run Code Online (Sandbox Code Playgroud)

但这似乎有点太长,而且很难实现看似简单的事情.我有什么想法可以改进我的代码?

ehi*_*ird 5

您只需Board使用模式匹配从构造函数中解包列表,然后将其包装回每个步骤; 例如,使用let...in:

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard cs =
    let Board css = genBoard (drop 3 cs)
    in Board (take 3 cs : css)
Run Code Online (Sandbox Code Playgroud)

或者,更具惯用性,一个where条款:

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard cs = Board (take 3 cs : css)
  where
    Board css = genBoard (drop 3 cs)
Run Code Online (Sandbox Code Playgroud)

另一个改进是使用模式匹配而不是takedrop:

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard (c0:c1:c2:cs) = Board $ [c0, c1, c2] : css
  where
    Board css = genBoard cs
Run Code Online (Sandbox Code Playgroud)

您还可以使用拆分包使其更简单:

genBoard :: [Cell] -> Board
genBoard = Board . splitEvery 3
Run Code Online (Sandbox Code Playgroud)