Эквивалент атопарсеков inClass в Парсеке - PullRequest
1 голос
/ 27 декабря 2011

Я перевожу некоторый код из attoparsec в Parsec, потому что парсер должен генерировать лучшие сообщения об ошибках. Код attoparsec широко использует inClassnotInClass). Существует ли аналогичная функция для Parsec, которая позволяет мне механически переводить inClass -поступления? Hayoo и Hoogle не предложили никакой информации по этому вопросу.

inClass :: String -> Char -> Bool

inClass "a-c'-)0-3-" эквивалентно \ x -> elem x "abc'()0123-", но последнее неэффективно и утомительно писать для больших диапазонов.

Я сам переопределю функцию, если ничего не доступно.

Ответы [ 2 ]

1 голос
/ 27 декабря 2011

Нет такого комбинатора;если бы он был, он был бы в Text.Parsec.Char (где определены все стандартные функции комбинатора синтаксического анализа, которые включают Char).Вы должны быть в состоянии определить его довольно легко.

Я не думаю, что вы сможете получить те же преимущества в производительности, которые attoparsec делает с его реализацией , однако;он опирается на внутренний тип FastSet, который работает только с 8-битными символами.Конечно, если вам не нужна поддержка Unicode, это может не быть проблемой, но код для FastSet подразумевает, что вы получите непредсказуемые результаты, передавая символы больше '\255', так что если выЕсли вы хотите повторно использовать решение на основе FastSet, вам, по крайней мере, придется читать строки, которые вы анализируете, в двоичном режиме .(Вам также нужно будет скопировать реализацию FastSet в вашу программу, так как она не экспортируется ...)

Если ваши строки диапазона короткие, то простое решение, подобное этому, вероятно, будет довольноfast:

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

(заботясь о том, чтобы убрать рекурсию из лямбды; я не знаю, может ли GHC сделать это сам.)

1 голос
/ 27 декабря 2011

Нет, в парсеке нет эквивалента. Вы должны написать это сами. Я вижу два основных варианта,

  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

- это простой подход.

...