Haskell:如何统计棋盘上指定颜色(忽略数值)的棋子数量?
我定义了以下Haskell数据类型:
data Player = Black | White deriving Show data Cell = Piece Player Int | Empty deriving Show type Board = [[Cell]]
以及如下Eq实例:
instance Eq Player where (==) Black Black = True (==) White White = True (==) _ _ = False
我希望统计当前棋盘(Board)上黑方(Black)棋子的数量。我知道若要查找特定棋子(如Piece Black 170)可使用如下代码:
getSpecificPiece :: Board -> [Player] getSpecificPiece b = concat (map (filter (== Piece Black 170)) b)
但我想忽略棋子的数值(Int),仅查找黑方(Black)的棋子,尝试了concat (map (filter (== Piece Black _ )) b)却无法正常工作,请问该如何实现?
你不能直接用== Piece Black _的原因是:_是模式匹配中的通配符,不能用来构造一个“模糊”的值参与相等性比较——==要求两边都是确定的表达式,而Piece Black _不是合法的表达式。
要实现忽略数值统计黑方棋子,有几种简洁的方式:
方法1:定义辅助判断函数
先写一个函数判断某个Cell是否是黑方棋子:
isBlackPiece :: Cell -> Bool isBlackPiece (Piece Black _) = True -- 匹配任意数值的黑棋 isBlackPiece _ = False -- 其他情况(白棋或空单元格)都返回False
然后利用这个函数过滤棋盘并统计数量:
countBlackPieces :: Board -> Int countBlackPieces = length . concatMap (filter isBlackPiece)
这里concatMap等价于concat . map,用来把二维的Board摊平成一维的Cell列表,再过滤出黑棋,最后用length得到数量。
方法2:用Lambda表达式直接过滤
如果不想单独定义辅助函数,可以在filter里直接写模式匹配的lambda:
countBlackPieces :: Board -> Int countBlackPieces board = length $ concatMap (filter (\cell -> case cell of Piece Black _ -> True _ -> False)) board
方法3:列表推导式
用列表推导式的模式匹配特性,直接收集所有黑棋对应的占位符,最后取长度:
countBlackPieces :: Board -> Int countBlackPieces board = length [() | row <- board, Piece Black _ <- row]
这里Piece Black _ <- row会自动过滤出当前行中的黑棋,每匹配到一个就生成一个(),最后列表的长度就是黑棋总数。
另外补充:你之前写的getSpecificPiece函数类型标注有错误——filter (== Piece Black 170)得到的是[Cell],concat之后还是[Cell],但你标注的返回类型是[Player],如果需要提取Player的话,应该改成:
getSpecificPiece :: Board -> [Player] getSpecificPiece b = [p | row <- b, Piece p 170 <- row]
内容的提问来源于stack exchange,提问作者tdeus

