如何使用attoparsec解析固定长度、非分隔整数?

3

我正在尝试使用attoparsec从3个字符中解析两个整数。示例输入可能如下所示:

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

实现这么简单的事情一定有更好的方法,我是不是漏掉了什么?


2
为什么你在两个解析器的开头都硬编码了一个 char '1'?这样无法解析输入样例 "341" - Dogbert
抱歉,我没有解释清楚。实际输入看起来像1341,但第一个字符只是随后数据类型的标志。 - Frank Wang
1个回答

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"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接