我试图区分解析器中的Ints和浮点数。我有2个解析器,每个int和float一个。但是,我无法在#39 ;.#39;上遇到失败。我寻找否定并展望未来,似乎没有得到结果。
我希望我不会重复任何问题。
我让它看着下一个不是'的字符。'但这是一个丑陋的解决方案。
编辑:添加了更多代码。
--Int--------------------------------------------------------------------
findInt :: Parser String
findInt = plus <|> minus <|> number
number :: Parser String
number = many1 digit
plus :: Parser String
plus = char '+' *> number
minus :: Parser String
minus = char '-' <:> number
makeInt :: Parser Int
makeInt = prepareResult (findInt <* many (noneOf ".") <* endOfLine)
where readInt = read :: String -> Int
prepareResult = liftA readInt
makeInt2 :: Parser Int
makeInt2 = do
numberFound <- (findInt <* many (noneOf ".") <* endOfLine)
match <- char '.'
return (prepareResult numberFound)
where readInt = read :: String -> Int
prepareResult = readInt
--End Int----------------------------------------------------------------
答案 0 :(得分:2)
我认为你最好将两个解析器组合成一个。尝试这样的事情:
import Text.Parsec.String (Parser)
import Control.Applicative ((<|>))
import Text.Parsec.Char (char,digit)
import Text.Parsec.Combinator (many1,optionMaybe)
makeIntOrFloat :: Parser (Either Int Float)
makeIntOrFloat = do
sign <- optionMaybe (char '-' <|> char '+')
n <- many1 digit
m <- optionMaybe (char '.' *> many1 digit)
return $ case (m,sign) of
(Nothing, Just '-') -> Left (negate (read n))
(Nothing, _) -> Left (read n)
(Just m, Just '-') -> Right (negate (read n + read m / 10.0^(length m)))
(Just m, _) -> Right (read n + read m / 10.0^(length m))
ErikR有一个正确的解决方案,但使用try
意味着parsec
必须跟踪回溯的可能性(这有点效率低),而事实上在这种情况下是不必要的
这里的关键区别在于我们实际上可以告诉我们是否有浮动 - 如果我们没有浮动,optionMaybe
中的char '.' *> many1 digit
解析器会立即失败(没有消耗输入),所以没有必要考虑回溯。
在GHCi
ghci> import Text.Parsec.Prim
ghci> parseTest makeIntOrFloat "1234.012"
Right 1234.012
ghci> parseTest makeIntOrFloat "1234"
Left 1234
答案 1 :(得分:1)
我会使用notFollowedBy
- 例如:
import Text.Parsec
import Text.Parsec.String
import Text.Parsec.Combinator
int :: Parser String
int = many1 digit <* notFollowedBy (char '.')
float :: Parser (String,String)
float = do whole <- many1 digit
fracpart <- try (char '.' *> many digit) <|> (return "")
return (whole, fracpart)
intOrFloat :: Parser (Either String (String,String))
intOrFloat = try (fmap Left int) <|> (fmap Right float)
test1 = parseTest (intOrFloat <* eof) "123"
test2 = parseTest (intOrFloat <* eof) "123.456"
test3 = parseTest (intOrFloat <* eof) "123."
答案 2 :(得分:0)
使用应用程序组合器来构建解析器通常最简单 - 这使得解析器更易于推理,并且通常不需要解析器的monadic和回溯函数。
例如,整数解析器可以这样写:
import Text.Parsec hiding ((<|>), optional)
import Text.Parsec.String
import Numeric.Natural
import Control.Applicative
import Data.Foldable
natural :: Parser Natural
natural = read <$> many1 digit
sign :: Num a => Parser (a -> a)
sign = asum [ id <$ char '+'
, negate <$ char '-'
, pure id
]
integer :: Parser Integer
integer = sign <*> (fromIntegral <$> natural)
十进制数是一个整数,可选地后跟一个小数部分(&#39;。&#39;后跟另一个整数),它本身就是一个数字,所以你的解析器可以写成
decimalPart :: Parser Double
decimalPart = read . ("0."++) <$> (char '.' *> many1 digit)
integerOrDecimal :: Parser (Either Integer Double)
integerOrDecimal = liftA2 cmb integer (optional decimalPart) where
cmb :: Integer -> Maybe Double -> Either Integer Double
cmb x Nothing = Left x
cmb x (Just d) = Right (fromIntegral x + d)
cmb
的定义很明显 - 如果不是小数部分,则生成Integer
,如果有,则生成Double
,通过将整数部分添加到小数部分。
您还可以根据上述内容定义小数的解析器:
decimal :: Parser Double
decimal = either fromIntegral id <$> integerOrDecimal
请注意,上述解析器都不直接使用monadic函数(即>>=
)或回溯 - 使它们变得简单而有效。