2

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

inClass :: String -> Char -> Bool

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

如果没有其他可用的,我将自己重新实现该功能。

4

2 回答 2

2

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

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

前者当然是后者的特例,如果你的班级有更长的射程,效率会更低。但这可能更容易实现。

(|||) :: (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

是一种简单的可能性。

于 2011-12-27T14:25:26.973 回答
2

没有任何这样的组合器;如果有,它将在Text.Parsec.Char中(其中定义了所有涉及的标准解析器组合器函数Char)。您应该能够相当容易地定义它。

不过,我认为您无法获得与 attoparsec实现相同的性能优势;它依赖于内部FastSet类型,该类型仅适用于 8 位字符。当然,如果您不需要 Unicode 支持,那可能不是问题,但代码FastSet暗示您将通过 Chars 大于 获得不可预测的结果'\255',因此如果您想重用FastSet基于 - 的解决方案,您将至少必须读取您以二进制模式解析的字符串。(您还必须将 的实现复制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;我不知道 GHC 是否可以/会自己这样做。)

于 2011-12-27T14:25:52.103 回答