在Parsec中相当于attoparsecs`inClass`

时间:2011-12-27 13:47:16

标签: haskell parsec attoparsec

我正在将一些代码从attoparsec转换为Parsec,因为解析器需要生成更好的错误消息。 attoparsec代码广泛使用inClass(和notInClass)。 Parsec有类似的功能可以让我翻译inClass - 机械地出现吗? Hayoo和Hoogle没有对此事提供任何见解。

inClass :: String -> Char -> Bool

inClass "a-c'-)0-3-"等同于\ x -> elem x "abc'()0123-",但后者对于大范围编写效率低且乏味。

如果没有别的东西,我会自己重新实现这个功能。

2 个答案:

答案 0 :(得分:1)

不,parsec中没有等价物。你必须自己写。我看到两个主要选项,

  1. 解析inClass语法,从中创建String,与oneOf一起使用
  2. 解析它以创建一个传递给satisfy
  3. 的函数

    前者当然是后者的一个特例,如果你的班级有更长的范围,那么效率就会降低。但它可能更容易实现。

    (|||) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
    p ||| q = \x -> p x || q x
    (&&&) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
    p &&& q = \x -> p x && q x
    
    parseClass (l:'-':h:more) = ((>= l) &&& (<= h)) ||| parseClass more
    parseClass (c:cs) = (== c) ||| parseClass cs
    parseClass [] = const False
    

    是一种笨笨的可能性。

答案 1 :(得分:1)

没有这样的组合器;如果存在,它将在Text.Parsec.Char中(这是定义涉及Char的所有标准解析器组合函数的地方)。你应该能够很容易地定义它。

我认为你无法获得attoparsec与its implementation相同的性能优势;它依赖于内部FastSet类型,该类型仅适用于8位字符。当然,如果你不需要Unicode支持,那可能不是问题,但是code for FastSet意味着你会得到不可预测的结果,通过大于'\255'的字符,所以如果你想重用基于FastSet的解决方案,您至少必须阅读您在binary mode中解析的字符串。 (您还必须将FastSet的实现复制到您的程序中,因为它未导出...)

如果您的范围字符串很短,那么像这样的简单解决方案可能会非常快:

type Range = (Char, Char)

inClass :: String -> Char -> Bool
inClass = inClass' . parseClass

parseClass :: String -> [Range]
parseClass "" = []
parseClass (a:'-':b:xs) = (a, b) : parseClass xs
parseClass (x:xs) = (x, x) : parseClass xs

inClass' :: [Range] -> Char -> Bool
inClass' cls c = any (\(a,b) -> c >= a && c <= b) cls

你甚至可以尝试这样的事情,至少与上述版本一样有效(包括多次调用单inClass s时),另外避免列表遍历开销:

inClass :: String -> Char -> Bool
inClass "" = const False
inClass (a:'-':b:xs) = \c -> (c >= a && c <= b) || f c where f = inClass xs
inClass (x:xs) = \c -> c == x || f c where f = inClass xs

(注意移动lambda的递归 out ;我不知道GHC是否可以/将自己这样做。)