2016-05-26 22 views
0

Dank Remove white space from string, kann ich die Leerzeichen in einer Zeichenfolge erfolgreich entfernen, aber in meinem Fall muss ich auch die Wörter trennen und sie alle in eine Liste wie im folgenden Beispiel.Entfernen von Leerzeichen aus einer Zeichenfolge und setzen jedes Wort getrennt in einer Liste, Haskell

Eingangs

"A \ t String mit vielen \ nSpaces."

ausgeben würde

[ "A", "String", "mit", "viele", "Räume".]

Ich bin in der Lage zu Ausgang dieses

["","A","","","","String","with","many"]

mit folgendem Code

Da ich versuche, Haskell zu lernen, wären Lösungen ohne andere Bibliotheken ideal!

+0

http://stackoverflow.com/questions/4978578/how-to-split-a-string-in-haskell Siehe das Komma ersetzen Trennzeichen mit einem Leerzeichen – ChrisF

+0

Ich hätte genauer sein sollen, aber ich suche eine Lösung ohne Verwendung von Bibliotheken. – user6386278

Antwort

1

Müssen Sie es selbst tun? Wenn nicht, verwenden Sie Data.String.words.

words :: String -> [String] 
words s = case dropWhile Char.isSpace s of 
        "" -> [] 
        s' -> w : words s'' 
         where (w, s'') = break Char.isSpace s' 

bearbeiten

:

λ words " A \t String with many\nspaces." 
["A","String","with","many","spaces."] :: [String] 

words ist definiert durch nicht Data.String Funktionen.

Sie waren nicht zu weit weg.

Zuerst fehlt das letzte Wort in Ihrer Ausgabe. Sie können das lösen, indem Sie die Zeile splitWord _ [] = [] in splitWord word [] = [word] ändern.

Das nächste Problem sind die leeren Zeichenfolgen, die der Liste hinzugefügt werden. Sie müssen sie herausfiltern (Ich habe eine Top-Level-Funktion zu demonstrieren):

addIfNotEmpty :: String -> [String] -> [String] 
addIfNotEmpty s l = if s == "" then l else s:l 

Mit dieser Funktion:

splitWord word [] = addIfNotEmpty word [] 
splitWord word ('\n':as) = addIfNotEmpty word $ splitWord "" as 
splitWord word ('\t':as) = addIfNotEmpty word $ splitWord "" as 
splitWord word (' ':as) = addIfNotEmpty word $ splitWord "" as 
splitWord word (a:as) = splitWord (word ++ [a]) as 

Und tadaa! Es klappt. Aber warte, wir sind noch nicht fertig!


Aufräumen

Lassen Sie uns von splitWords starten. Nicht viel zu tun, aber wir können eta-reduction verwenden:

splitWords :: String -> [String] 
splitWords = splitWord "" 

Als nächstes feststellen, dass für jede Art von Raum, die Aktion ist das gleiche.Lassen Sie uns die Wiederholung entfernen:

splitWord word (c:cs) 
    | c `elem` " \t\n" = addIfNotEmpty word $ splitWord "" cs 
    | otherwise  = splitWord (word ++ [c]) cs 

I elem hier verwendet um zu überprüfen, ob das nächste Zeichen ein Raum ist, gibt es wohl bessere Möglichkeiten, es zu tun.

Endergebnis:

splitWords :: String -> [String] 
splitWords = splitWord "" 

splitWord :: String -> String -> [String] 
splitWord word [] = addIfNotEmpty word [] 
splitWord word (c:cs) 
    | c `elem` " \t\n" = addIfNotEmpty word $ splitWord "" cs 
    | otherwise  = splitWord (word ++ [c]) cs 

addIfNotEmpty :: String -> [String] -> [String] 
addIfNotEmpty s l = if s == "" then l else s:l 
+0

Ja, ich möchte versuchen, Bibliotheken zu vermeiden! – user6386278

+0

Hallo, danke für deine tolle Antwort! Ich habe eine Frage über die Eta-Reduktion, die Sie dort versuchen wollen. Es wäre schön, wenn es einen Fall für die leere Liste gäbe, aber wenn ich das versuche, sagt es, dass es verschiedene Mengen von Argumenten hat, wie dieses splitWords [] = []. Gibt es eine Möglichkeit, diesen Fall in splitWords einzufügen? – user6386278

+0

@ user6386278 Sie brauchen keinen Fall für die leere Liste. Ich bin nicht überzeugt, dass es auch nett wäre, meistens verwirrend. 'splitWords' benötigt zum Argument eine' String', keine 'List'. Es würde funktionieren, weil "String" äquivalent zu "[Char]" ist, aber es würde Dinge verwirren. Das Aufrufen von 'splitWords' mit' "' 'oder' [] 'führt zu demselben Ergebnis, aber die Verwendung einer Zeichenfolge ist konsistenter. Wenn Sie 'splitWords '" 'aufrufen, ruft es' splitWord "" "" '' auf, was mit 'splitWord word []' übereinstimmt. Beachten Sie, dass ich selbst den Fehler gemacht habe, '[]' zu verwenden, wobei '" "' geeigneter wäre. –

1

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."] 
Verwandte Themen