2016-05-21 3 views
0

ich Haskell zu lernen, versuche ich, über folgende Frage steckeHaskell rekursive Funktion, die eine Folge von Wörtern in einzelne Zeichenfolge teilt

eine Funktion Split Schreiben Sie :: rawText -> [Wort] dieses spaltet eine bestimmte Zeichenkette in eine Liste von Wörtern.

Um die Dinge einfach zu halten, nehmen wir an, dass der rohe Text nur eine Zeichenfolge aus Buchstaben und Leerzeichen ist, möglicherweise mit einem unregelmäßigen Abstand. Zum Beispiel: Split "eins zwei drei ff" = ["eins", "zwei", "drei", "ff"]

Ich bekomme immer ["one" *** Ausnahme: recursion.hs: 30 : 1-14: Nicht erschöpfende Muster in Funktion verschüttet ', so dass die Rekursion niemals endet, aber ich habe einen Basisfall.

Meine Funktion

type RawText = String 
-- [Char] 'a','b','c','d','e' 
type SingleWord = String 
type Line = [SingleWord] 
type Page = [Line] 

--  [-------CHAR-----] 
-- split " one two three ff " = ["one","two","three","ff"] 
--        [char],[char],[char],[char] 
-- " " is delimiter for a [] 

split' :: RawText -> [SingleWord] 
spilt' [] = [] 
split' xs 
    | xs == [] = [] 
    | isBlank (head xs) = split' xs 
    | otherwise = waitForBlank xs : spilt' (tail xs) 

isBlank :: Char -> Bool 
isBlank x = if x == ' ' then True else False 

waitForBlank :: String -> String 
waitForBlank [] = [] 
waitForBlank (x:xs) 
    | isBlank x = [] 
    | otherwise = x : waitForBlank xs 
+1

Aktivieren Warnungen hier hätte geholfen, die Tippfehler 'spilt' in Spek, Erzeugen einer Nachricht wie„Signatur fehlt für 'spilt'“. – chi

Antwort

0

Meine Lösung (Feedback willkommen, Neuling hier)

split' :: RawText -> [SingleWord] 
split' [] = [] 
split' (x:xs) 
    | isBlank x = split' xs 
    | otherwise = waitForBlank (x:xs) : split' (drop (length (waitForBlank (x:xs))) xs) 

isBlank :: Char -> Bool 
isBlank x = if x == ' ' then True else False 

waitForBlank :: String -> String 
waitForBlank [] = [] 
waitForBlank (x:xs) 
    | isBlank x = [] 
    | otherwise = x : waitForBlank xs 
+1

Glückwunsch, du hast es verstanden! Diese Lösung mag funktionieren, aber sie kann verbessert werden, indem eine Bibliotheksfunktion verwendet wird, die eine explizite Rekursion eliminiert (was von vielen funktionalen Programmierern als unangenehm empfunden wird). In meiner Antwort finden Sie ein Beispiel für die Verwendung vordefinierter Funktionen. – ThreeFx

5

Sie deklarieren eine Funktion split' und eine Funktion spilt'. Korrigiere die Tippfehler und du solltest gut gehen!

split' :: RawText -> [SingleWord] 
spilt' [] = [] -- the first typo is here 
split' xs 
    | xs == [] = [] 
    | isBlank (head xs) = split' xs 
    | otherwise = waitForBlank xs : spilt' (tail xs) -- here is the second typo 

die Meldung zu erklären, ist es am besten Ihren Code in die beiden unterschiedlichen Funktionen aufteilen Sie erklärt:

split' :: RawText -> [SingleWord] 
split' xs 
    | xs == [] = [] 
    | isBlank (head xs) = split' xs 
    | otherwise = waitForBlank xs : spilt' (tail xs) 

-- spilt' :: [a] -> [a] (or something like this) 
spilt' [] = [] 

Wenn Sie jetzt in split' Rekursion, rufen Sie spilt'. Da die von Ihnen deklarierte Funktion spilt' die nicht leere Liste nicht behandelt, löst sie eine Ausnahme aus.


Auf einer weiteren Notiz, wenn Sie die Tippfehler korrigieren, werden Sie nicht die leere Liste zweimal behandeln müssen:

import Prelude hiding (split) 

split :: RawText -> [SingleWord] 
split [] = [] 
split xs 
    -- | xs == [] = [] this case is matched by the pattern 
    --     above and thus not needed 
    | isBlank (head xs) = split' xs 
    | otherwise = waitForBlank xs : split (tail xs) 

Auch wenn Muster auf Listen übereinstimmt, können Sie explizit Mustererkennung gegen die head und tail der Liste durch die Faust cons Anwendung auszuschreiben ausdrücklich:

split [email protected](x:xs) 
    | isBlank x = split' xs 
    | otherwise = waitForBlank s : split' xs 

Aber irgendwie diese Funktion noch se ems irgendwie klobig, es muss einen besseren Weg geben, alle Buchstaben zu überspringen. Werfen wir einen Blick auf die Bibliotheksfunktionen, um zu sehen, was uns helfen könnte. Sie finden sie here:

-- drops letters from RawText while a certain predicate holds 
dropWhile :: (a -> Bool) -> [a] -> [a] 
-- takes letters form RawText while a certain predicate holds 
takeWhile :: (a -> Bool) -> [a] -> [a] 

Diese sehen eher vielversprechend aus. Jetzt können wir waitForBlank umschreiben als:

waitForBlank xs = takeWhile (not . isBlank) xs 
-- or, pointfree: 
waitForBlank = takeWhile (not . isBlank) 

dropWhile verwenden, können wir auch machen die split Funktion effizienter, indem sie alle Nicht-Leerzeichen übersprungen wird. Beachten Sie, dass der Speicherplatz in dem Teil von dropWhile enthalten ist und explizit gelöscht werden muss.

split :: RawText -> [SingleWord] 
split [] = [] 
split xs = waitForBlank xs : split (dropUntilBlank xs) 

-- With dropUntilBlank defined as 
dropUntilBlank xs = tail (dropWhile (not . isBlank) xs) 

-- without the call of tail, dropUntilBlank would keep the space in between the words: 
dropWhile (not . isBlank) "a word" => " word" 
--    note the extra space: ^^^ 

-- using tail on this now produces the correct word: 
tail (dropWhile (not . isBlank) "a word") = "word" 

Jetzt sieht das Ergebnis schön und sauber:

split :: RawText -> [SingleWord] 
split [] = [] 
split xs = waitForBlank xs : split (dropUntilBlank xs) 

waitForBlank xs = takeWhile (not . isBlank) xs 

dropUntilBlank xs = tail (dropWhile (not . isBlank) xs) 

Der letzte Teil auch mit break kombiniert werden können:

split :: RawText -> [SingleWord] 
split [] = [] 
split xs = word : split' rest 
    where 
     (word, (space:rest)) = break isBlank xs 

Diese Antwort geht davon aus, dass alle Wörter durch ein getrennt einzelnes Leerzeichen. Für mehrere Leerzeichen muss dropUntilBlank neu geschrieben werden, um mehrere Leerzeichen auszuschließen, indem tail für dropWhile isBlank ersetzt wird.

+0

Danke, für die Rückmeldung +1 – dijam

Verwandte Themen