Was wir brauchen, ist ein Parser. Dies ist einfach eine Funktion, die eine Zeichenfolge als Eingabe verwendet und eine Datenstruktur als Ausgabe zurückgibt. Ich zeige Ihnen einen vereinfachten Weg, um einen Parser im "Kombinator" -Stil zu erstellen. Was das bedeutet ist, dass wir den Parser, den wir wollen, aus kleineren Parsern erstellen (indem wir sie kombinieren).
Dies ist nicht der beste oder effizienteste Weg, um dies zu tun, aber es wird die Technik demonstrieren. Und es benötigt keine Bibliotheken!
Wir werden mit einer Sprache Pragma beginnen, einige Textvorschlag zu verringern:
{-# LANGUAGE DeriveFunctor #-}
Lassen Sie uns nun einen Datentyp erstellen Parsing-Funktionen darzustellen.
data Parser a = P { parser :: String -> Maybe (String, a) } deriving Functor
Grundsätzlich ist der Parser eine Funktion unterhalb des Data Wrapper. Die Art und Weise, wie es funktioniert, wird eine Zeichenkette als Eingabe annehmen und wenn seine Kriterien Zeichen am Anfang der Zeichenkette entsprechen, dann wird es diese Zeichen verbrauchen, die Daten des Typs a
erzeugen und eine Just
zurückgeben, die die unverbrauchte Eingabe und die neue enthält Artikel. Wenn das Kriterium jedoch fehlschlägt, gibt es einfach Nothing
zurück.
Wir werden Applicative und Monad für unseren Parser-Typ implementieren, dann können wir Do-Notation verwenden. Dies ist eines der coolsten Features von Haskell (IMHO). Wir werden den Applicative <*>
nicht verwenden, aber wir brauchen die Instanz, um Monad zu implementieren. (Obwohl Applicative ist super in seinem eigenen Recht.)
instance Applicative Parser where
pure x = P (\input -> Just (input, x))
f <*> p = do
f' <- f
p' <- p
return $ f' p'
Die Tastaturfunktion Monad erfordert, ist die bind (>>=
), die das Ergebnis eines ersten Parsers nimmt und führt es eine Funktion, die einen zweiten Parser zurückzugibt. Dies ist die bequemste Art, Parser zu kombinieren. Dadurch können wir Ergebnisse akkumulieren (oder wegwerfen), ohne die Eingabe manuell durch die Parserfunktionen zu führen.
instance Monad Parser where
return = pure
p >>= f = P (\input -> case parse p input of
Just (rest, x) -> parse (f x) rest
_ -> Nothing)
Als nächstes brauchen wir einen Weg, um einen "primitiven" Parser zu erstellen. Wir machen eine Funktion, die ein Char
Prädikat übernimmt und einen Parser zurückgibt, der ein einzelnes Zeichen akzeptiert, das das Prädikat übergibt.
Es gibt viele andere Möglichkeiten, Parser zu manipulieren, aber wir bleiben bei der Lösung des Problems. Das nächste, was wir brauchen, ist eine Möglichkeit, einen Parser zu wiederholen. Hier kommt die while
Funktion zum Einsatz. Es dauert ein Parser, der Artikel vom Typ a
erzeugt und wiederholt, bis es fehlschlägt, die Ergebnisse in einer Liste akkumulieren.
while :: Parser a -> Parser [a]
while p = P (\input -> case parse p input of
Nothing -> Just (input, [])
Just (rest, x) -> parse (fmap (x:) (while p)) rest)
Wir sind fast fertig. Wir erstellen die Prädikate, um Whitespaces von Nicht-Whitespaces zu unterscheiden.
isWhitespace c = c == ' ' || c == '\t' || c == '\n'
isNotWhiteSpace = not . isWhitespace
Ok, jetzt werden wir sehen, wie fantastisch Do-Notation ist.Zuerst erstellen wir einen Parser für ein einzelnes Wort.
word :: Parser String
word = do
c <- (satisfy isNotWhitespace) -- grab the first character
cs <- while (satisfy isNotWhitespace) -- get any other characters
while (satisfy isWhitespace) -- eat the trailing whitespace
return (c:cs)
Wir können endlich den Parser implementieren, den wir wirklich wollen!
splitWords :: Parser [String]
splitWords = do
while (satisfy isWhitespace) -- eat up any leading whitespace
while word
Und schließlich, versuchen Sie es!
main :: IO()
main = do
let input = " A \t String with many\nspaces."
case parse splitWords input of
Nothing -> putStrLn "failed!"
Just (_, result) -> putStrLn . show $ result
Dies ist, was ich in GHCI erhalten:
λ main
["A","String","with","many","spaces."]
http://stackoverflow.com/questions/4978578/how-to-split-a-string-in-haskell Siehe das Komma ersetzen Trennzeichen mit einem Leerzeichen – ChrisF
Ich hätte genauer sein sollen, aber ich suche eine Lösung ohne Verwendung von Bibliotheken. – user6386278