Haskell Parser Combinators(ReadP)-如果失败,则返回整个列表,否则只返回通过的列表
我有一个字符串,例如mmmmabcnnnxyzppppppp。我知道这个字符串中可能有ABC,也可能有XYZ,但这两个字符串都不是必需的。此外,XYZ可以替换为DEF,例如MMMMabcnndefpppp,其行为应保持不变 我想解析字符串并返回它们之间的序列,以及存在哪个XYZ或DEF。例如:Haskell Parser Combinators(ReadP)-如果失败,则返回整个列表,否则只返回通过的列表,haskell,Haskell,我有一个字符串,例如mmmmabcnnnxyzppppppp。我知道这个字符串中可能有ABC,也可能有XYZ,但这两个字符串都不是必需的。此外,XYZ可以替换为DEF,例如MMMMabcnndefpppp,其行为应保持不变 我想解析字符串并返回它们之间的序列,以及存在哪个XYZ或DEF。例如: data Divider1 = Abc data Divider2 = Xyz | Def --"MMMMABCNNNXYZPPPPP" should return ("MMMM", Just Abc,
data Divider1 = Abc
data Divider2 = Xyz | Def
--"MMMMABCNNNXYZPPPPP" should return ("MMMM", Just Abc, "NNN", Just Xyz, "PPPPP")
--"MMMMABCNNNDEFPPPPP" should return ("MMMM", Just Abc, "NNN", Just Def, "PPPPP")
请注意,如果ABC不存在,我希望返回除法器2之前的所有内容,如果XYZ和DEF都不存在,我希望返回除法器1之后的所有内容
例如:
--"MMMMNNNXYZPPPPP" should return ("MMMM", Nothing, "NNN", Just Xyz, "PPPPP")
--"MMMMABCNNNPPPPP" should return ("MMMM", Just Abc, "NNN", Nothing, "PPPPP")
如果ABC和XYZ都不存在,那么我不在乎它是否什么也不返回,或者它是否返回整个字符串
目前我的代码是
parseEverything = many $ satisfy someGeneralCondition--check if all characters are valid
parseAbc = (\str -> Abc) <$> string "ABC"
parseXyz = (\str -> Xyz) <$> string "XYZ"
parseDef = (\str -> Def) <$> string "DEF"
parseFull = do
beforeAbc <- gather parseEverything
parseAbc <- (Just <$> parseAbc) <++ return Nothing
beforeDivider2 <- gather parseEverything
parseDivider2 <- (Just <$> parseXyz) <++ (Just <$> parseDef) <++ (Just <$> Nothing)
everythingElse <- look
return (beforeAbc, parseAbc, beforeDivider2, parseDivider2, everythingElse)
但是,当我在示例字符串MMMMABCNNNXYZPPPPP上运行它时,我得到的结果大多是失败的,只有一个我想要的结果。问题是,如果parseAbc失败,我需要在beforeAbc中返回所有内容,但是如果parseAbc通过,那么我只需要返回它。parseXyz和parseDef也是如此。我认为没有更新:请参见下面关于应用程序解析器的注释
以下是您当前方法的问题所在。毫无疑问,您知道,Text.ParserCombinators.ReadP中的解析器生成字符串所有可能前缀的所有可能有效解析。如果编写解析器:
letterAndOther = do
letters <- many (satisfy isLetter)
others <- many get
return (letters, others)
换句话说,在do块中,每个一元操作通常会生成一个可能的解析树。在当前代码中,do块的第一行:
beforeAbc <- gather parseEverything
这是明确的,涵盖了所有可能性。诚然,在前两个构造函数中包含Divider1是多余的,因为只有一个可能的Divider1,但程序也是供人阅读的,保持Divider1显式可以提高可读性
现在,让我们为第一个和第二个除法器定义解析器:
divider1 = Abc <$ string "ABC"
divider2 = (Def <$ string "DEF") +++ (Xyz <$ string "XYZ")
现在,让我们为完整字符串编写一个解析器。这里的一个关键洞察是,我们有四个备选方案,即结果类型的四个构造函数。它们确实有一些共同的结构,但是解析器的第一个漏洞可以将它们作为独立的替代品来处理。我们将使用I not know readp来回答这个问题,但是您可能很高兴了解到,您可以简单地编写y,而不是\x->y z,这很好。y在内部是否与\x->y相同?我认为它们的类型是不同的。不,不同的是@FyodorSoikin抱歉,我无意中更改了帖子中的名称,它们实际上是大写的。在解析MMMMNNNXYZPPPPP的示例中,当它们之间没有分隔符时,解析器如何确定MMMM和NNN应该拆分?哇,感谢您对这个非常好的回答!我添加了一个关于应用程序解析器的注释。一旦你习惯了它们,它们通常比它们对应的一元形式更容易书写和阅读/理解。当你可以使用我在回答中提到的除法器时,为什么要在结果中包含除法器:诚然,在前两个构造函数中包含除法器1是多余的,因为只有一个可能的除法器1,但程序也是供人阅读的,保持Divider1的显式可提高可读性。显然,在这一点上存在分歧。
parseAbc <- (Just <$> parseAbc) <++ return Nothing
data Result
= TwoDividers String Divider1 String Divider2 String
| FirstDivider String Divider1 String
| SecondDivider String Divider2 String
| NoDividers String
divider1 = Abc <$ string "ABC"
divider2 = (Def <$ string "DEF") +++ (Xyz <$ string "XYZ")
anything = many $ satisfy isLetter -- valid characters
result =
(TwoDividers <$> anything <*> divider1 <*> anything <*> divider2 <*> anything)
<++ (FirstDivider <$> anything <*> divider1 <*> anything)
<++ (SecondDivider <$> anything <*> divider2 <*> anything)
<++ (NoDividers <$> anything)
> readP_to_S result "MMMMABCNNNXYZPPPPP"
[(TwoDividers "MMMM" Abc "NNN" Xyz "","PPPPP"),...]
parseResult = readP_to_S (result <* eof)
main = mapM_ (print . parseResult)
[ "MMMMABCNNNXYZPPPPP"
, "MMMMABCNNNDEFPPPPP"
, "MMMMNNNXYZPPPPP"
, "MMMMABCNNNPPPPP"
]
[(TwoDividers "MMMM" Abc "NNN" Xyz "PPPPP","")]
[(TwoDividers "MMMM" Abc "NNN" Def "PPPPP","")]
[(SecondDivider "MMMMNNN" Xyz "PPPPP","")]
[(FirstDivider "MMMM" Abc "NNNPPPPP","")]
FirstDivider <$> anything <*> divider1 <*> anything
someParser <* eof
let x = 1 + 5
data Statement = ... | Let Var Expr | ...
statement = ...
<|> Let <$ string "let" <*> var <* symbol "=" <*> expr
...
do string "let"
v <- var
symbol "="
e <- expr
return $ Let v e
import Data.Char
import Text.ParserCombinators.ReadP
data Divider1 = Abc deriving (Show)
data Divider2 = Xyz | Def deriving (Show)
data Result
= TwoDividers String Divider1 String Divider2 String
| FirstDivider String Divider1 String
| SecondDivider String Divider2 String
| NoDividers String
deriving (Show)
anything :: ReadP String
anything = many $ satisfy isLetter -- valid characters
divider1 :: ReadP Divider1
divider1 = Abc <$ string "ABC"
divider2 :: ReadP Divider2
divider2 = (Def <$ string "DEF") +++ (Xyz <$ string "XYZ")
result :: ReadP Result
result =
(TwoDividers <$> anything <*> divider1 <*> anything <*> divider2 <*> anything)
<++ (FirstDivider <$> anything <*> divider1 <*> anything)
<++ (SecondDivider <$> anything <*> divider2 <*> anything)
<++ (NoDividers <$> anything)
parseResult :: String -> [(Result, String)]
parseResult = readP_to_S (result <* eof)
main :: IO ()
main = mapM_ (print . parseResult)
[ "MMMMABCNNNXYZPPPPP"
, "MMMMABCNNNDEFPPPPP"
, "MMMMNNNXYZPPPPP"
, "MMMMABCNNNPPPPP"
]