我有以下类型检查:
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)
(如果你很好奇,这是一个文件格式的解析器,其中包含蛋白质中与氢键合的β链对。我无法控制文件格式!)
答案 0 :(得分:7)
如何让Parsec让我拨打
read :: Int
?
第二个答案是“不要使用阅读”。
使用read
相当于重新解析已经解析过的数据 - 因此在Parsec解析器中使用它是一种代码味道。解析自然数是无害的,但是read
与Parsec有不同的失败语义,它适合Haskell的词法语法,因此将它用于更复杂的数字格式是有问题的。
如果您不想解决定义LanguageDef
和使用Parsec的Token
模块的麻烦,这里是一个不使用read的自然数字解析器:
-- | Needs @foldl'@ from Data.List and
-- @digitToInt@ from Data.Char.
--
positiveNatural :: Stream s m Char => ParsecT s u m Int
positiveNatural =
foldl' (\a i -> a * 10 + digitToInt i) 0 <$> many1 digit
答案 1 :(得分:5)
p_int
是一个生成Int
的解析器,因此类型为Parser Int
或类似的¹。
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
。
¹ParsecT x y z Int
。
答案 2 :(得分:1)
你可能会找到
Text-Megaparsec-Lexer.integer :: MonadParsec s m Char => m Integer
做你想做的事。
vanilla parsec库似乎缺少一些明显的解析器,这导致了“包含电池”parsec衍生包的兴起。我想parsec维护者最终会找到好东西。
https://hackage.haskell.org/package/megaparsec-4.2.0/docs/Text-Megaparsec-Lexer.html
更新
或与vanilla parsec:
Prelude Text.Parsec Text.Parsec.Language Text.Parsec.Token> parse ( integer . makeTokenParser $ haskellStyle ) "integer" "-1234"
Right (-1234)