Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.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
Haskell Parser Combinators(ReadP)-如果失败,则返回整个列表,否则只返回通过的列表_Haskell - Fatal编程技术网

Haskell Parser Combinators(ReadP)-如果失败,则返回整个列表,否则只返回通过的列表

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,

我有一个字符串,例如mmmmabcnnnxyzppppppp。我知道这个字符串中可能有ABC,也可能有XYZ,但这两个字符串都不是必需的。此外,XYZ可以替换为DEF,例如MMMMabcnndefpppp,其行为应保持不变

我想解析字符串并返回它们之间的序列,以及存在哪个XYZ或DEF。例如:

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"
  ]