Haskell: снятие функции чтения с парсера parsec - PullRequest
4 голосов
/ 25 августа 2010

В рамках 4-го упражнения здесь Я хотел бы использовать функцию типа reads, например readHex с парсеком Parser.

. Для этого у меня естьнаписали функцию:

liftReadsToParse :: Parser String -> (String -> [(a, String)]) -> Parser a
liftReadsToParse p f = p >>= \s -> if null (f s) then fail "No parse" else (return . fst . head ) (f s)

которая может использоваться, например, в GHCI, например:

*Main Numeric> parse (liftReadsToParse (many1 hexDigit) readHex) "" "a1"
Right 161

Может кто-нибудь предложить какое-либо улучшение этого подхода в отношении:

Будет ли термин (f s) запоминаться или оцениваться дважды в случае возврата null (f s) False? Обработка нескольких успешных разборов, т.е. когда length (f s) больше единицы, я не знаю, как parsec справляется с этим. Обработка оставшейся части разбора, т.е. (snd . head) (f s).

Ответы [ 2 ]

3 голосов
/ 26 августа 2010

Это хорошая идея. Более естественный подход, который сделал бы Ваш ReadS парсер лучше подходит для Parsec пропустите Parser String в начале типа:

liftReadS :: ReadS a -> String -> Parser a
liftReadS reader = maybe (unexpected "no parse") (return . fst) .
                   listToMaybe . filter (null . snd) . reader

Этот стиль "комбинатор" очень идиоматичен для Haskell - как только вы привыкнуть к нему, это делает определения функций намного проще читать и понимать.

Затем вы должны использовать liftReadS в следующем простом случае:

> parse (many1 hexDigit >>= liftReadS readHex) "" "a1"

(Обратите внимание, что listToMaybe находится в модуле Data.Maybe.)

В более сложных случаях liftReadS легко использовать внутри любого Парсек do блок.

Относительно некоторых других ваших вопросов:

  1. Функция reader применяется только один раз, поэтому «незаписывать» нечего.
  2. В большинстве случаев принято игнорировать все, кроме первого анализа в синтаксическом анализаторе ReadS, так что все в порядке.
0 голосов
/ 25 августа 2010

Чтобы ответить на первую часть вашего вопроса, нет (f s) не будет запомнено, вам придется сделать это вручную:

liftReadsToParse p f = p >>= \s -> let fs = f s in if null fs then fail "No parse"
                                                              else (return . fst . head ) fs

Но вместо этого я бы использовал сопоставление с образцом:

liftReadsToParse p f = p >>= \s -> case f s of
                                        []              -> fail "No parse"
                                        (answer, _) : _ -> return answer
...