假设我有以下数据类型:
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
但这似乎有点太长,而且很难实现看似简单的事情。我有什么想法可以改进我的代码吗?
答案 0 :(得分:5)
您只需使用模式匹配从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)
另一项改进是使用模式匹配而不是take
和drop
:
genBoard :: [Cell] -> Board
genBoard [] = []
genBoard (c0:c1:c2:cs) = Board $ [c0, c1, c2] : css
where
Board css = genBoard cs
您还可以使用split包更简单:
genBoard :: [Cell] -> Board
genBoard = Board . splitEvery 3
答案 1 :(得分:2)
您可以使用提升功能将[[Cell]]
上的功能转换为Board
上的功能:
liftBoard :: ([[Cell]] -> [[Cell]]) -> Board -> Board
liftBoard f (Board css) = Board (f css)
通过此操作,您可以将(take 3 c) :
转换为可以在递归Board
调用返回的genBoard
上使用的内容:
genBoard :: [Cell] -> Board
genBoard [] = Board []
genBoard c = liftBoard (take 3 c :) $ genBoard (drop 3 c)
答案 2 :(得分:1)
请注意,对于只是围绕其他类型的包装的类型,使用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)