Как я могу анализировать целые числа фиксированной длины без разделителей с помощью attoparsec?

Я пытаюсь разобрать два целых числа из 3 символов, используя attoparsec. Пример ввода может выглядеть примерно так:

341

... который я хотел бы разобрать на:

Constructor 34 1

У меня есть два решения, которые работают, но несколько неуклюжи:

stdK :: P.Parser Packet
stdK = do
    P.char '1'
    qstr <- P.take 2
    let q = rExt $ P.parseOnly P.decimal qstr
    n <- P.decimal
    return $ Std q n

stdK2 :: P.Parser Packet
stdK2 = do
    P.char '1'
    qn <- P.decimal
    let q = div qn 10
    let n = rem qn 10
    return $ Std q n

Должен быть лучший способ добиться чего-то столь же простого, как это. Я что-то упускаю?


person Frank Wang    schedule 08.06.2015    source источник
comment
Почему у вас жестко закодировано char '1' в начале обоих парсеров? Это не проанализирует ваш образец ввода "341".   -  person Dogbert    schedule 08.06.2015
comment
Извините, я этого не объяснил. Фактический ввод будет выглядеть как 1341, но первый символ — это просто флаг для типа данных, которые следуют.   -  person Frank Wang    schedule 08.06.2015


Ответы (1)


Ваш фрагмент кода далеко не самодостаточен (в частности, отсутствует импорт и определение вашего типа данных Packet), но вы, похоже, слишком усложняете ситуацию.

Во-первых, определите синтаксический анализатор для одноразрядных целых чисел. Затем используйте последний как строительный блок для синтаксического анализатора двузначных целых чисел. После этого используйте аппликативные операторы, чтобы объединить эти два синтаксических анализатора и определить синтаксический анализатор для вашего пользовательского типа данных Packet. Смотри ниже.

Обратите внимание, что вам не нужна вся мощь монад; здесь достаточно аппликативного разбора.

-- test_attoparsec.hs

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative ((<$>))
import Data.Attoparsec.Text
import Data.Char

data Packet = Std {-# UNPACK #-} !Int
                  {-# UNPACK #-} !Int
  deriving (Show)

stdK :: Parser Packet
stdK = char '1' *> (Std <$> twoDigitInt <*> oneDigitInt)

twoDigitInt :: Parser Int
twoDigitInt = timesTenPlus <$> oneDigitInt <*> oneDigitInt
  where
    timesTenPlus x y = 10 * x + y

oneDigitInt :: Parser Int
oneDigitInt = digitToInt <$> digit

Тесты в GHCi:

λ> :l test_attoparsec.hs
[1 of 1] Compiling Main             ( test_attoparsec.hs, interpreted )
Ok, modules loaded: Main.

λ> :set -XOverloadedStrings 

λ> parseOnly stdK "1341"
Right (Std 34 1)

λ> parseOnly stdK "212"
Left "1: Failed reading: satisfyWith"
person jub0bs    schedule 12.06.2015