Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parsing 带状态的Haskell-Parsec_Parsing_Haskell_Parsec - Fatal编程技术网

Parsing 带状态的Haskell-Parsec

Parsing 带状态的Haskell-Parsec,parsing,haskell,parsec,Parsing,Haskell,Parsec,我有一个文件,其中游戏状态以String格式保存。此字符串由移动列表组成,以,分隔。从这个移动列表中,我必须重建游戏状态。因此,从概念上讲,对于我解析的每一步,我都想适当地修改游戏状态,并将这个游戏状态传递给下一步的解析。从概念上讲,这相当于在开始时有一个空列表,并且对于每个移动,都考虑到解析后的移动到该列表。最后,您应该有一个包含所有解析移动的列表 我将下面的代码示例作为一个简化版本来解析alfabetic字母,并将它们推到列表上。我想学习的核心概念是如何拥有初始状态,在每个解析周期中传递该状

我有一个文件,其中游戏状态以
String
格式保存。此字符串由移动列表组成,以
分隔。从这个移动列表中,我必须重建游戏状态。因此,从概念上讲,对于我解析的每一步,我都想适当地修改游戏状态,并将这个游戏状态传递给下一步的解析。从概念上讲,这相当于在开始时有一个空列表,并且对于每个移动,都考虑到解析后的移动到该列表。最后,您应该有一个包含所有解析移动的列表

我将下面的代码示例作为一个简化版本来解析alfabetic字母,并将它们推到列表上。我想学习的核心概念是如何拥有初始状态,在每个解析周期中传递该状态,并使用parsec返回最终状态
someState
最初是空列表

parseExample :: State -> Parser [Char]
parseExample someState = do spaces 
                            c <- char 
                            c : someState
                            return someState
parseExample::State->Parser[Char]
parseExample someState=do空格

c您可能想使用foldl(如果我正确理解您的问题)。因此,您将得到如下函数:

module Main where

import Data.Text
import Data.String

main :: IO ()
main =
  putStrLn (show $ parseGameState "a, b, c")

data State = State deriving (Show)

parseGameState :: String -> [State]
parseGameState stateString = parsedState where
  parsedState = Prelude.foldl mkNewStateFromPreviousAndMove [] moves where
    moves = splitOn (fromString ",") (fromString stateString)
    mkNewStateFromPreviousAndMove oldStates move = oldStates ++ [newState previousState move] where
      previousState = Prelude.last oldStates
      newState previousState move = State
这样做的目的是:

将CSV移动字符串作为输入

然后将该字符串拆分为移动字符串列表

然后,我们从一个空列表开始,通过将mkNewStateFromPreviousAndMove应用于移动列表中的每个元素以及由fold构建的列表的最后一个元素,将移动字符串折叠到此列表中

注意,您需要将以下DEP添加到package.yaml文件(如果使用堆栈):

  • 正文

此dep用于拆分字符串。

将“状态”合并到解析器中的最简单方法是根本不这样做。假设我们有一个tic-tac趾板:

data Piece = X | O | N deriving (Show)
type Board = [[Piece]]
要分析移动列表,请执行以下操作:

X11,O00,X01
data Move = Move Piece Int Int
moves :: Parser [Move]
moves = sepBy move (char ',')
  where move = Move <$> piece <*> num <*> num
        piece = X <$ char 'X' <|> O <$ char 'O'
        num = read . (:[]) <$> digit
moves' :: Parser' ()
moves' = sepBy move' (char ',')
进入代表游戏状态的棋盘
[[O,X,N],[N,X,N],[N,N,N]]

 O | X |
---+---+---
   | X |
---+---+---
   |   |
board0 :: Board
board0 = [[N,N,N],[N,N,N],[N,N,N]]

game :: [Move] -> Board
game = foldl' turn board0

turn :: Board -> Move -> Board
turn brd (Move p r c) = brd & ix r . ix c .~ p
loadGame' :: String -> Board
loadGame' str =
  case runParser (moves' >> getState) [[N,N,N],[N,N,N],[N,N,N]] "" str of
    Left err -> error $ "parse error: " ++ show err
    Right brd -> brd
我们可以分离解析器,它只生成一个移动列表:

X11,O00,X01
data Move = Move Piece Int Int
moves :: Parser [Move]
moves = sepBy move (char ',')
  where move = Move <$> piece <*> num <*> num
        piece = X <$ char 'X' <|> O <$ char 'O'
        num = read . (:[]) <$> digit
moves' :: Parser' ()
moves' = sepBy move' (char ',')
然后在
loadGame
功能中将它们连接在一起:

loadGame :: String -> Board
loadGame str =
  case parse moves "" str of
    Left err -> error $ "parse error: " ++ show err
    Right mvs -> game mvs
这应该是此类问题的解决方案:首先解析为一个简单的无状态中间形式,然后在“有状态”计算中处理这个中间形式

如果您真的想在解析过程中建立状态,有几种方法可以做到这一点。在这种特殊情况下,根据上述
turn
的定义,我们可以通过将
game
函数中的折叠合并到解析器中,直接解析为
Board

moves1 :: Parser Board
moves1 = foldl' turn board0 <$> sepBy move (char ',')
  where move = Move <$> piece <*> num <*> num
        piece = X <$ char 'X' <|> O <$ char 'O'
        num = read . (:[]) <$> digit
然后是修改用户状态的单个移动的解析器:

type Parser' = Parsec String Board
move' :: Parser' ()
move' = do
  m <- Move <$> piece <*> num <*> num
  modifyState (flip turn m)
  where piece = X <$ char 'X' <|> O <$ char 'O'
        num = read . (:[]) <$> digit
将生成最终游戏状态:

 O | X |
---+---+---
   | X |
---+---+---
   |   |
board0 :: Board
board0 = [[N,N,N],[N,N,N],[N,N,N]]

game :: [Move] -> Board
game = foldl' turn board0

turn :: Board -> Move -> Board
turn brd (Move p r c) = brd & ix r . ix c .~ p
loadGame' :: String -> Board
loadGame' str =
  case runParser (moves' >> getState) [[N,N,N],[N,N,N],[N,N,N]] "" str of
    Left err -> error $ "parse error: " ++ show err
    Right brd -> brd
在这里,
loadGame'
使用
moves'
在用户状态上运行解析器,然后使用
getState
调用来获取最终板

由于
ParsecT
是一个单变量转换器,因此一个几乎等价的解决方案是创建一个
ParsecT。。。(州议会)
monad变压器堆栈,带有标准的
State
层。例如:

type Parser'' = ParsecT String () (Control.Monad.State.State Board)

move'' :: Parser'' ()
move'' = do
  m <- Move <$> piece <*> num <*> num
  modify (flip turn m)
  where piece = X <$ char 'X' <|> O <$ char 'O'
        num = read . (:[]) <$> digit

moves'' :: Parser'' ()
moves'' = void $ sepBy move'' (char ',')

loadGame'' :: String -> Board
loadGame'' str =
  case runState (runParserT moves'' () "" str) board0 of
    (Left err, _)   -> error $ "parse error: " ++ show err
    (Right (), brd) -> brd

据我所知,在本例中您没有使用parsec,对吗?这将是一个要求,因为我的其他功能是用那些解析器组合器实现的。嗯,我不是,但你说:“我想学习的核心概念是如何拥有一个初始状态,在每个解析周期中传递它并返回最终状态。”,我想(我不是haskell专家)您需要的工具是
foldl
。你会想在我发布的函数的最后一行使用Parsec,以便实际解析给定的移动字符串(如果这是你计划使用它的目的?)。嘿,刚刚做了一些研究,我想你可能需要
sebby
。见: