2016-08-20 7 views
1

Ich versuche zu unterscheiden zwischen Ints und Floats in einem Parser. Ich habe 2 Parser für jeden Int und Float. Allerdings habe ich Schwierigkeiten, auf einem '.' Zu scheitern. Ich suchte nach Negieren und sah nach vorne und schien nicht zu bekommen und Früchte.Parsec, wenn eine Übereinstimmung gefunden wird, dann Fehler werfen

Ich hoffe, dass ich keine Fragen duplizieren.

Ich hatte es mit der Suche nach dem nächsten Zeichen, das kein '.' aber das ist eine hässliche Lösung.

BEARBEITEN: Zusätzlicher Code hinzugefügt.

--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

Wie sieht 'findInt' aus (hilft nur etwas zu arbeiten)? – Alec

+0

Hinzugefügt. Tut mir leid, ich habe nicht nachgedacht, als ich den Code aufgesetzt habe. –

Antwort

2

Ich glaube, Sie sind am besten aus, die beiden Parser in eine tatsächlich zu kombinieren. so etwas wie dies versuche:

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 eine richtige Lösung, aber die Verwendung von try bedeutet, dass parsec Spur der Möglichkeit zu halten hat den Rückzieher (was ein wenig ineffizient ist), wenn in der Tat, die in dieser nicht erforderlich ist, Fall.

Hier ist der entscheidende Unterschied, dass wir tatsächlich sofort sagen können, wenn wir einen Schwimmer oder nicht haben - wenn wir so gibt es keinen Schwimmer haben, werden die char '.' *> many1 digit Parser in optionMaybe sofort fehlschlagen (ohne Eingabe raubend), Es ist nicht notwendig, Backtracking in Betracht zu ziehen.

Bei GHCi

ghci> import Text.Parsec.Prim 
ghci> parseTest makeIntOrFloat "1234.012" 
Right 1234.012 
ghci> parseTest makeIntOrFloat "1234" 
Left 1234 
1

würde ich notFollowedBy - z.B .:

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." 
0

Es ist in der Regel am einfachsten applicative combinators zu verwenden, um Ihre Parser zu bauen - dies Grund Ihre Parser erleichtert über und oft Sie monadische und Backtracking-Funktionen des Parsers nicht brauchen.

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) 

Dezimalzahl eine ganze Zahl ist, gegebenenfalls durch einen Dezimalteil (a, gefolgt von einer anderen ganzen Zahl ‚‘), gefolgt, die selbst:

Zum Beispiel könnte ein Parser für ganze Zahlen als solche geschrieben eine Zahl richtig, kann so Ihre Parser als

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) 

die Definition von cmb liegt auf der Hand geschrieben werden - wenn das kein Dezimalteil ist, dann ein Integer produzieren, und wenn es ein Double, durch Zugabe des integ produzieren er Teil zum Dezimalteil.

Sie können auch einen Parser für Dezimalzahlen in Bezug auf die oben definieren:

decimal :: Parser Double 
decimal = either fromIntegral id <$> integerOrDecimal 

Beachten Sie, dass keine der oben Parser direkt monadischen Funktionen (das heißt >>=) oder Rückzieher verwenden - sie einfach und effizient zu machen.

Verwandte Themen