Вопрос по haskell – Как мне заставить Parsec позволить мне вызывать `read` :: Int?

2

Я получил следующие проверки типов:

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' '))

Теперь, как следует из названия функции, я хочу, чтобы оно давало мне Int. Но если я сделаю это:

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Int

Я получаю эту ошибку типа:

Couldn't match expected type `Int' with actual type `f0 b0'
In the return type of a call of `liftA'
In the expression:
    liftA read (many (char ' ') *> many1 digit <* many (char ' ')) ::
      Int
In an equation for `p_int':
    p_int
      = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) ::
          Int

Есть ли более простой и понятный способ анализа целых чисел, которые могут иметь пробелы? Или способ это исправить?

В конечном итоге я хочу, чтобы это было частью следующего:

betaLine = string "BETA " *> p_int <*> p_int  <*> p_int <*>
           p_int <*> p_parallel <*> p_exposure <* eol

который должен разобрать строки, которые выглядят так:

BETA  6 11 5 24 -1 oiiio

Таким образом, я могу в конечном итоге вызвать конструктор BetaPair, который будет нуждаться в этих значениях (некоторые как Int, некоторые как другие типы, такие как [Exposure] и Parallel)

(если вам интересно, это парсер для формата файла, который представляет собой, среди прочего, пары бета-цепей с водородными связями в белках. У меня нет контроля над форматом файла!)

Ваш Ответ

3   ответа
1

Вы можете найти

Text-Megaparsec-Lexer.integer :: MonadParsec s m Char => m Integer

делает то, что вы хотите.

В библиотеке vanilla parsec, по-видимому, отсутствует ряд очевидных синтаксических анализаторов, что привело к увеличению количества «включенных батарей». производные пакеты parsec. Я полагаю, что в конце концов сопровождающие парсек обойдутся с лучшими.

https://hackage.haskell.org/package/megaparsec-4.2.0/docs/Text-Megaparsec-Lexer.html

ОБНОВИТЬ

или с ванильным парсеком:

Prelude Text.Parsec Text.Parsec.Language Text.Parsec.Token> parse ( integer . makeTokenParser $ haskellStyle ) "integer" "-1234"
Right (-1234)
7

How do I get Parsec to let me call read :: Int?

Вторым ответом является «Не используйте чтение».

С помощьюread эквивалентно повторному анализу данных, которые вы уже проанализировали, поэтому использование его в анализаторе Parsec является запахом кода. Разбор натуральных чисел достаточно безвреден, ноread имеет семантику сбоя, отличную от Parsec, и она адаптирована к лексическому синтаксису Haskell, поэтому использование его для более сложных числовых форматов проблематично.

Если вы не хотите пытаться определитьLanguageDef и используя парсекToken Модуль здесь - это анализатор натуральных чисел, который не использует read:

-- | Needs @foldl'@ from Data.List and 
-- @[email protected] from Data.Char.
--
positiveNatural :: Stream s m Char => ParsecT s u m Int
positiveNatural = 
    foldl' (\a i -> a * 10 + digitToInt i) 0 <$> many1 digit
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Noah Daniels
5

p_int это парсер, который производитIntтак что тип будетParser Int или аналогичный & # xB9 ;.

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Parser Int

Кроме того, вы можете ввестиread функция,(read :: String -> Int) сообщить компилятору, какой тип имеет выражение.

p_int = liftA (read :: String -> Int) (many (char ' ') *> many1 digit <* many (char ' ')) :: Int

Что касается более чистых способов, рассмотрите возможность заменыmany (char ' ') сspaces.

& # XB9;ParsecT x y z Int, например.

Error: User Rate Limit Exceeded Noah Daniels

Похожие вопросы