繁体   English   中英

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

[英]Haskell : How do I compose a recursive function that takes an element and gives gives back its list, but having different data type?

假设我有如下数据类型:

 data Cell = Cell (Maybe Player)
 data Board = Board [[Cell]]

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

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

显然上面的代码失败了,因为(:)不能将[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

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

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

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard cs =
    let Board css = genBoard (drop 3 cs)
    in Board (take 3 cs : css)

或者,更具惯用性, where子句:

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard cs = Board (take 3 cs : css)
  where
    Board css = genBoard (drop 3 cs)

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

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard (c0:c1:c2:cs) = Board $ [c0, c1, c2] : css
  where
    Board css = genBoard cs

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

genBoard :: [Cell] -> Board
genBoard = Board . splitEvery 3

您可以使用提升功能将[[Cell]]功能转换为Board功能:

liftBoard :: ([[Cell]] -> [[Cell]]) -> Board -> Board
liftBoard f (Board css) = Board (f css)

有了这个,您可以将(take 3 c) :转换为可以在递归genBoard调用返回的Board上使用的内容:

genBoard :: [Cell] -> Board
genBoard [] = Board []
genBoard c  = liftBoard (take 3 c :) $ genBoard (drop 3 c)

请注意,对于只是围绕其他类型的包装的类型,最好使用newtype(没有运行时开销,newtypes只是一个编译时工具):

newtype Board = Board [[Cell]]

您也可以使用记录语法为您制作“解构器”:

newtype Board = Board { boardContent :: [[Cell]] }

然后你可以将[[Cell]]上的一个函数“提升”到板上的一个函数:

liftBoard f = Board . f . boardContent

genBoard cs  = liftBoard (take 3 cs :) (genBoard (drop 3 cs))

但是,如果您只使用类型同义词,那么根本不需要。 您真的需要一个适用于您的主板的数据类型,或者您只是为了方便和文档而给它起了一个名字? 使用newtype或数据的好处是你不能混合[[Cell]]和Board,但你真的使用[[Cell]]来做除了你的电路板以外的任何事情吗? 如果您使用类型同义词,您也无法在板上定义新实例,这有什么困扰吗?

如果两个问题的答案都是否定的,那么只需使用:

type Board = [[Cell]]

genBoard :: [Cell] -> Board
genBoard [] = []
genBoard cs =  take 3 cs : genBoard (drop 3 cs)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM