如何避免在解析FEN字符串生成棋盘时使用大量模式匹配?
如何避免在解析FEN字符串生成棋盘时使用大量模式匹配?
我太懂你这种被一堆模式匹配搞得头大的感觉了——原来的代码不仅写起来繁琐,以后要改规则或者扩展的时候,简直是灾难。咱们可以换个思路,把解析过程拆成几个小模块,每个模块只负责一件事,这样代码会清爽很多,也不用写那么多重复的匹配分支。
首先先明确咱们要处理的FEN规则:
- 整个字符串用
/分隔3行,对应3x3棋盘的每一行 - 每行内部用
,分隔3个单元格 - 每个单元格里的
w/b字符序列对应玩家栈,空字符串(比如连续的,,中间的空)就是空单元格
接下来咱们一步步实现更优雅的解析:
第一步:准备基础定义与依赖
先把你的数据类型定义好,再导入需要的工具函数(这里用Data.List.Split的splitOn来拆分字符串,用traverse来批量转换字符):
import Data.List.Split (splitOn) import Data.Traversable (traverse) data Player = Red | Blue deriving Show data Cell = Stack [Player] | Empty deriving Show type Board = [[Cell]]
第二步:字符到玩家的映射
写一个简单的函数,把w/b转换成对应的Player类型,处理无效字符的情况:
charToPlayer :: Char -> Maybe Player charToPlayer 'w' = Just Red -- 你可以根据实际需求调整映射,比如w对应White如果Player定义是White/Black charToPlayer 'b' = Just Blue charToPlayer _ = Nothing -- 遇到非w/b的字符返回空,后续可以转成Empty单元格
第三步:单元格字符串转Cell
把单个单元格的字符串转换成Cell:空字符串直接返回Empty,非空字符串则把每个字符转成Player,再包装成Stack:
cellStrToCell :: String -> Cell cellStrToCell "" = Empty cellStrToCell s = case traverse charToPlayer s of Just players -> Stack players Nothing -> Empty -- 遇到无效字符时返回空单元格,也可以改成抛出错误提示
第四步:行字符串转单元格列表
把一行的字符串按,拆分后,逐个转换成Cell:
rowStrToCells :: String -> [Cell] rowStrToCells = map cellStrToCell . splitOn ","
第五步:生成完整棋盘
最后把整个FEN字符串按/拆分成行,再把每行转换成单元格列表,就得到了完整的Board:
fenToBoard :: String -> Board fenToBoard = map rowStrToCells . splitOn "/"
可选:增加格式合法性验证
如果想确保生成的棋盘是严格的3x3格式,可以把函数改成返回Maybe Board,做长度检查:
fenToBoard :: String -> Maybe Board fenToBoard s = let rows = map rowStrToCells $ splitOn "/" s in if length rows == 3 && all (\row -> length row == 3) rows then Just rows else Nothing
为什么这种方法更好?
- 职责单一:每个函数只做一件事,逻辑清晰,调试和修改都很方便
- 避免冗余匹配:不用再写一堆硬编码的
(a:b:c:d:rest)这种模式,不管单元格里的栈有多长,都能正确处理 - 扩展性强:以后如果要加新的玩家类型(比如
g代表Green),只需要修改charToPlayer函数就行,不用动其他代码 - 容错性好:可以轻松处理无效字符或者格式错误的情况,返回合理的结果或者错误提示
比如测试你给的例子:输入bwb,,b/,,/w,w,w,fenToBoard会生成:
[ [Stack [Blue,Red,Blue], Empty, Stack [Blue]] , [Empty, Empty, Empty] , [Stack [Red], Stack [Red], Stack [Red]] ]
完全符合预期。
备注:内容来源于stack exchange,提问作者h4sk3lln00b
相关产品推荐
相关产品推荐

