- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个文件,其中游戏状态保存在 String
中。格式。该字符串由一系列移动组成,由 ,
分隔。 .从这个 Action 列表中,我必须重建游戏状态。因此,从概念上讲,对于我解析的每一步,我想适本地修改游戏状态并将这个游戏状态传递给下一步的解析。从概念上讲,这可能等同于在开始时有一个空列表,并且对于每个移动都将解析的移动到该列表。最后,您应该有一个包含所有解析 Action 的列表。
我将下面的代码示例作为一个简化版本来解析 alfabetic 字母并将它们推送到列表中。我想学习的核心概念是如何拥有一个初始状态,为每个解析周期传递它并使用 parsec 返回最终状态。 someState
最初是空列表。
parseExample :: State -> Parser [Char]
parseExample someState = do spaces
c <- char
c : someState
return someState
最佳答案
将“状态”合并到解析器中的最简单方法是根本不这样做。假设我们有一个井字棋盘:
data Piece = X | O | N deriving (Show)
type Board = [[Piece]]
X11,O00,X01
[[O,X,N],[N,X,N],[N,N,N]]
代表游戏状态:
O | X |
---+---+---
| X |
---+---+---
| |
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
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
功能:
loadGame :: String -> Board
loadGame str =
case parse moves "" str of
Left err -> error $ "parse error: " ++ show err
Right mvs -> game mvs
turn
的定义上面,我们可以直接解析成
Board
通过合并来自
game
的折叠函数进入解析器:
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
Board
定义解析器用户状态:
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
move'
的返回类型是
()
因为它的操作是作为对用户状态的副作用实现的。
moves' :: Parser' ()
moves' = sepBy move' (char ',')
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
是一个monadtransformer,是创建一个
ParsecT ... (State Board)
具有标准的 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
infixr 8 **
时行,您可以修改状态以正确解析后续表达式。使用用户状态来实际构建解析的结果不是预期的用途。
import Control.Lens
import Control.Monad
import Control.Monad.State
import Data.Foldable
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.String
data Piece = X | O | N deriving (Show)
type Board = [[Piece]]
data Move = Move Piece Int Int
-- *Standard parsing approach
moves :: Parser [Move]
moves = sepBy move (char ',')
where move = Move <$> piece <*> num <*> num
piece = X <$ char 'X' <|> O <$ char 'O'
num = read . (:[]) <$> digit
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 parse moves "" str of
Left err -> error $ "parse error: " ++ show err
Right mvs -> game mvs
-- *Incoporate fold into parser
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
-- *Non-standard effectful parser
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
moves' :: Parser' ()
moves' = void $ sepBy move' (char ',')
loadGame' :: String -> Board
loadGame' str =
case runParser (moves' >> getState) board0 "" str of
Left err -> error $ "parse error: " ++ show err
Right brd -> brd
-- *Monad transformer stack
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
-- *Tests
main = do
print $ loadGame "X11,O00,X01"
print $ loadGame' "X11,O00,X01"
print $ loadGame'' "X11,O00,X01"
关于parsing - Haskell - Parsec 与状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59681272/
Haskell Parsec 的 indents 包提供了一种解析缩进式语言(如 Haskell 和 Python)的方法。它重新定义了 Parser类型,那么如何使用 Parsec 的 Text.P
我有一个用 Text 编写的解析器作为流类型,而默认为 Text.Parsec.String模块使用 String除此以外。 如何在 Parsec Text b c 的上下文中使用自定义编写的解析器
Text Text.Parsec Text.Parsec.ByteString Text.Parsec.ByteString.Lazy
我在弄清楚如何使用 Text.Parsec.Indent 中的任何功能时遇到问题 indents 提供的模块Haskell 的包,它是 Parsec 的一种附加组件。 所有这些功能有什么作用?它们将如
Text Text.Parsec Text.Parsec.ByteString Text.Parsec.ByteString.Lazy
我目前正在尝试使用现实世界 Haskell 中提供的完整 CSV 解析器。为了我尝试修改代码以使用 ByteString 而不是 String,但是有一个 string 组合器仅适用于 字符串。 是否
我正在尝试解析嵌套的类 C block 注释 import Text.ParserCombinators.Parsec import Control.Monad (liftM) flat :: Mon
如果违反语义规则,使用 Parsec 如何在特定位置指示错误。我知道通常我们不想做这样的事情,但考虑一下示例语法。 ::= | ... ::= a positive integer power
我目前正在尝试使用 Parsec 在 Haskell 中设计一个解析器。声明类型的语法应如下所示: Fruit is a Apple 类型也应该能够有参数: Fruit a b is a Apple
我写了这样的排列解析例子: data Entry = Entry { first_name :: String , last_name :: String , date_of_birt
我正在尝试解析(目前)Dot 语言的一个子集。 语法是here我的代码如下 import System.Environment import System.IO import qualified Te
我有一个列表,我需要解析除最后一个元素之外的所有元素都需要由一个解析器解析,最后一个元素需要由另一个解析器解析。 a = "p1 p1b ... p2" or a = "p2" 原来我试过 parse
我需要为一种编程语言创建一个解析器。到目前为止,它已经完成了 95%,我想说,除了一个小细节。 用这种语言编写的程序具有以下结构: outputs inputs expressions 要求是输出不能
在我的工作中,我遇到了很多粗糙的 sql,我有一个聪明的想法,那就是编写一个程序来解析 sql 并整齐地打印出来。我很快就完成了大部分工作,但遇到了一个我不知道如何解决的问题。 所以让我们假设 sql
我正在编写一种使用 Parsec 进行解析的编程语言。为了报告错误消息,我使用 getPosition 将语法树的每个元素都标记了其源位置。函数来自 the Pos module 秒差距。 但是,它只
我正在尝试使用 parsec 解析如下表达式: f a b c => (Appl (Appl (Appl f a) b) c) 我尝试使用以下内容: appl :: Parser Expr appl
是否有可能以某种方式获得某些自定义类型的解析错误?例如,从错误中获取更多关于解析上下文的信息会很酷。而且仅以短信的形式提供错误信息似乎不太方便。 最佳答案 正如 Rhymoid 所观察到的,不幸的是,
我对 Parsec 的一个常见问题是,如果无效输入出现在“正确”的位置,它往往会忽略它。 作为一个具体的例子,假设我们有 integer :: Parser Int ,我写 expression =
我正在使用 Parsec 解析表达式,并且我想使用 Parsec 中的用户状态来跟踪这些表达式中的变量。不幸的是,我真的不知道该怎么做。 给定以下代码: import Data.Set as Set
我正在尝试编写一个 Haksell Parsec Parser,它将文件中的输入数据解析为 LogLine 数据类型,如下所示: --Final parser that holds the indvi
我是一名优秀的程序员,十分优秀!