haskell 如何避免与Parsing匹配?

ktca8awb  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(128)

我正在编写一个函数,它可以用FEN表示法(从国际象棋中得知)获取一个字符串。一个空的3x 3棋盘将是:

,,/,,/,,

字符串
在每个单元格上,可以有多个块相互堆叠,可以是白色(w)或黑色(b)。例如,有效位置为:

bwb,,b/,,/w,w,w


我使用以下数据类型:

data Player = Red | Blue deriving Show
data Cell = Stack [Player] | Empty deriving Show
type Board = [[Cell]]


我需要将一个FEN_String转换成一个board. I想首先将原始字符串拆分成'/'上的单独列表,但后来我陷入了如何以最好的方式处理结果列表的困境。
还有什么比大小写匹配更好的吗?我不想涵盖每一种可能的情况,我已经为一个不同的游戏做过一次了:

buildBoard :: String -> Board
buildBoard [] = [[]]
buildBoard xs =   (chunksOf 9 ([Empty] ++ buildboardHelper xs))

buildboardHelper :: String -> [Cell]
buildboardHelper (a:b:c:d:rest)
      | (a == ',' && b == 'w') = buildboardHelper (b:c:d:rest) 
      | (a == ',' && b == 'b') = buildboardHelper (b:c:d:rest)
      | (a == '/' && b == ',') = [Empty]  ++ buildboardHelper (b:c:d:rest)
      | (a == '/' && b == 'w') = buildboardHelper (b:c:d:rest)
      | (a == '/' && b == 'b') = buildboardHelper (b:c:d:rest)
      | (a == ',' && b == '/') = [Empty]  ++ buildboardHelper (b:c:d:rest)
      | (a == ',' && b == ',' && c == ',' && d ==',') = [Empty] ++ [Empty] ++ buildboardHelper (c:d:rest)
      | (a == ',' && b == ',') = [Empty] ++ buildboardHelper (b:c:d:rest)
      | (a == 'w' && isDigit b && isDigit c && isDigit d) = let number = (read ([b,c,d]) :: Int) in [Piece White number] ++ buildboardHelper (rest)
      | (a == 'w' && isDigit b && isDigit c) = let number = (read ([b,c]) :: Int) in [Piece White number] ++ buildboardHelper (d:rest)
      | (a == 'w' && isDigit b) = let number = (read ([b]) :: Int) in [Piece White number] ++ buildboardHelper (c:d:rest)
      | (a == 'b' && isDigit b && isDigit c && isDigit d) = let number = (read ([b,c,d]) :: Int) in [Piece Black number] ++ buildboardHelper (rest)
      | (a == 'b' && isDigit b && isDigit c) = let number = (read ([b,c]) :: Int) in [Piece Black number] ++ buildboardHelper (d:rest)
      | (a == 'b' && isDigit b) = let number = (read ([b]) :: Int) in [Piece Black number] ++ buildboardHelper (c:d:rest)
      | otherwise = [Empty]
buildboardHelper [',',',']  = [Empty] ++ [Empty]
buildboardHelper [','] = [Empty]

ykejflvf

ykejflvf1#

还有什么比大小写匹配更好的吗?我不想涵盖每一种可能的情况,我已经为一个不同的游戏做过一次了:
我强烈建议使用解析器库,而不是手动解析。这只会让情况变得更糟。手动解析通常很复杂:有很多情况需要覆盖,大多数手动解析器不允许在另一个解析器中轻松重用逻辑。
例如,我们可以使用parsec。这个库允许将非常简单的解析器合并组合成更复杂的解析器。
例如,我们可以首先为单个Player项创建一个解析器:

import Text.Parsec(Stream, ParsecT)
import Text.Parsec.Char(oneOf)

_player :: Char -> Player
_player 'w' = Red
_player 'b' = Blue

player :: Stream s m Char => ParsecT s u m Player
player = _player <$> oneOf "wb"

字符串
现在我们可以使用Cellplayer解析器:

import Text.Parsec(many)

_cell :: [Player] -> Cell
_cell [] = Empty
_cell cs = Stack cs

cell :: Stream s m Char => ParsecT s u m Cell
cell = _cell <$> many player


最后,我们为Board创建一个解析器:

import Text.Parsec(sepBy)
import Text.Parsec.Char(char)

board :: Stream s m Char => ParsecT s u m Board
board = sepBy (sepBy cell (char ',')) (char '/')


现在我们定义了解析器,我们可以运行board解析器:

ghci> parse board "" "bwb,,b/,,/w,w,w"
Right [[Stack [Blue,Red,Blue],Empty,Stack [Blue]],[Empty,Empty,Empty],[Stack [Red],Stack [Red],Stack [Red]]]


因此,它返回一个Right …,这意味着解析是成功的,乍一看,它看起来正确地解析了流。
最好的事情可能是,我们不仅得到了Board的解析器,而且还得到了CellPlayer的解析器,我们可以独立使用和合并。

相关问题