Haskell Parsec-解析两个内容列表

Haskell Parsec-解析两个内容列表,haskell,parsec,Haskell,Parsec,我用它作为学习如何使用Parsec的借口,但我在如何处理这个具体案例上遇到了障碍 输入采用以下格式: Before: [3, 2, 3, 0] 2 3 1 1 After: [3, 2, 3, 0] Before: [1, 0, 2, 1] 7 0 1 1 After: [1, 1, 2, 1] ... Before: [0, 0, 2, 1] 6 2 3 1 After: [0, 6, 2, 1] 5 0 2 3 5 1 3 1 ... 5 3 2 2 换句话说,首先是由三

我用它作为学习如何使用Parsec的借口,但我在如何处理这个具体案例上遇到了障碍

输入采用以下格式:

Before: [3, 2, 3, 0]
2 3 1 1
After:  [3, 2, 3, 0]

Before: [1, 0, 2, 1]
7 0 1 1
After:  [1, 1, 2, 1]

...

Before: [0, 0, 2, 1]
6 2 3 1
After:  [0, 6, 2, 1]



5 0 2 3
5 1 3 1
...
5 3 2 2
换句话说,首先是由三行组成的若干组,它们被解析为一个结构,由空行分隔,然后是三个空行,然后是若干四位数的行

我对每个结构都有可用的解析器-
Sample
maskedoOperation
,分别有解析器
Sample
maskedOp
,但我不知道如何将它们放在一起解析成
([Sample],[maskedoOperation])

我尝试了以下方法:

parseInput :: GenParser Char st ([Sample], [MaskedOperation])
parseInput = do
  samples <- sample `sepBy` (count 2 newline) <* count 3 newline
  operations <- maskedOp `sepBy` newline
  return (samples, operations)
我如何告诉parsec我希望尽可能多地使用分隔符(额外的换行符),然后开始阅读其他内容



1.阅读出现的代码问题,了解上下文;名字不重要。

恐怕您不能在这里使用
sebby

让我们将其简化为将“a”解析为示例,将“b”解析为换行符。您将像这样解析字符串

a b b b c b c b

那么,在遍历这样一个字符串时,解析器是怎么想的呢?让我们看一下:


解析
a
或空序列

[a]b a b c b c b

哦,一个
a
。序列不是空的,因此我将从现在开始解析
many
“bb”>>“a”

a[b a]b c b

成功!让我们再吃一点或者吃完

a b a[b b]b c b

好的,我找到了另一个
bb
,所以序列继续。解析
a

ababab[b]cbcbcbcbcb

沃特


问题是,解析器不会回滚,除非明确要求这样做。 若要提供解析器回滚功能,必须使用
try
combinator对其进行标记,否则它将无法“取消使用”已消耗的输入

目前,我认为没有比重写
sebby
组合器更好的方法来让它知道,在解析每个分隔符之后,如果解析
separator>>目标
失败,它可能需要将其返回缓冲区:

sepTry a sep = sepTry1 a sep <|> pure []
sepTry1 a sep = liftA2 (:) a (many (try $ sep *> a))
sepTry a sep=sepTry1 a sep纯[]
Septry1a sep=liftA2(:)a(许多(尝试$sep*>a))
请注意,解析
a
必须包含在
try
部分–这是实际触发故障的地方

为了将差异可视化,让我们看一看相同的场景,但改用
sepTry


a[b a]b c b

成功!如果可能的话,让我们再试一次

a![b]b c b

好的,我找到了另一个
bb
,所以序列继续。解析
a

a!b b[b]c b

不是我所期望的。返回失败并将光标移回感叹号

a![]b b c b

解析bba失败,解析序列已完成。解析
bbb

a b a[b b b]c b c b

成功



在每个
Sample
之后的情况下,此解析器将尝试读取后面有
Sample
的2个换行符,或者在失败的情况下读取3个换行符并继续执行
maskedoOperation
s

我所做的是首先运行一个标记化步骤,然后使用
解析器字符串([Sample],[Instruction])
而不是
解析器字符([Sample],[Instruction])
。将列表标记化后,可以更容易地忽略空格和其他不相关的标点符号

solve = parse . tokenize 
  where tokenize = concatMap words . lines . filter (not . (`elem` ",:[]"))
        parse = (,) <$> many sample <*> many instr
        sample = -- ...
        instr = -- ...
solve=parse。标记化
其中tokenize=concatMap单词。线筛选器(非(`elem`',:[]))
parse=(,)多样本多指令
样本=。。。
仪器=--。。。
不需要回溯,因为样本和指令仅与它们的第一个令牌不同

solve = parse . tokenize 
  where tokenize = concatMap words . lines . filter (not . (`elem` ",:[]"))
        parse = (,) <$> many sample <*> many instr
        sample = -- ...
        instr = -- ...