解析器:当订单未知时执行单一性

时间:2016-09-14 15:38:05

标签: haskell attoparsec

使用Attoparsec,我正在尝试匹配包含1' x',1' y'和1' z'以及任意数量&的字符串#39; a',' b'或' c',对每个字符的顺序没有任何限制。

例如," abbb z ac y aaa x cba"和" abbb z ac x aaa y cba"应该匹配,但以下不应该:

  • " ABBB的ž AC的ý aaacba" (原因:没有' x')
  • " ABBB的ž AC的ý AAA X CB的 X 一个" (原因:重复' x')

我能做到的最好成绩是:

import qualified Data.Attoparsec.ByteString.Char8 as A8
import qualified Data.ByteString.Char8 as B8 (pack)

p ch = do
    abcs <- A8.many' (A8.choice [A8.char 'a', A8.char 'b', A8.char 'c'])
    x    <- A8.char ch
    return $ concat [[x],abcs]

parse = do
    xyz1 <- A8.choice [p 'x', p 'y', p 'z']
    xyz2 <- A8.choice [p 'x', p 'y', p 'z']
    xyz3 <- A8.choice [p 'x', p 'y', p 'z']
    final <- A8.manyTill (A8.choice [A8.char 'a', A8.char 'b', A8.char 'c']) $ A8.char '\n'
    return (xyz1, xyz2, xyz3, final)

(任意地,我选择停止使用&#39; \ n&#39;解析,但这只是为了选择一个简单的例子。)

然后尝试ghci:

Prelude> A8.parseTest parse $ B8.pack "abbbzacyaaaxcba\n"
Done "" ("zabbb","yac","xaaa", "cba")
Prelude> A8.parseTest parse $ B8.pack "abbbzacyaaacba\n"
Fail "aaacba\n" [] "Failed reading: empty"
Prelude> A8.parseTest parse $ B8.pack "abbbzacyaaaxcbxa\n"
Fail "xa\n" [] "Failed reading: empty"

但它看起来非常笨重,而且它不容易扩展到一个独特字符列表(例如,我给了一个给定的char :: [Char]列表没有重复,我想匹配所有给定的字符串和任何&#39;&#39; b&#39;,&#39; c,介于两者之间,无论如何。)

有更好,更优雅,可扩展的方法吗?

PS:我不是在寻找正则表达式解决方案,因为它不适用于我的现实问题。我需要使用解析器。

1 个答案:

答案 0 :(得分:2)

您的代码存在一些问题:

首先,它不会在字符串中强制执行一个xyz。例如,它将接受xxx\n

其次,效率非常低。考虑在给定字符串xyz1

时如何解析aaaaaaaaaz
  1. 我们首先尝试p 'x'。首先解析所有a个字符,然后找到z。由于z不是x,因此整个p 'x'解析器都会失败。

  2. 然后我们尝试p 'y'。这将重新解析所有a个字符,然后再次找到z。由于z不是y,整个p 'y'解析器都会失败。

  3. 第三次尝试我们成功,只是在第三次重新解析所有a个字符后。

  4. 最好写一些类似的东西:

    parse = do
       s <- A8.takeWhile (\x -> elem x "abcxyz")
       let xs = count 'x' s
           ys = count 'y' s
           zs = count 'z' s
       guard $ xs == 1 && ys == 1 && zs == 1
       return s
    

    函数count来自Data.ByteString.Char8guard来自Control.Monad