tags:

views:

105

answers:

4

So I have this code here:

toWords :: String -> [String]
toWords "" = []
toWords (nr1 : rest)
    | nr1 == ' ' = toWords rest
    | otherwise = [nr1] : toWords rest

The "toWords" function should simply remove all spaces and return a list with all the words. But this happens:

*Main> toWords "How are you?"

["H","o","w","a","r","e","y","o","u","?"]

+3  A: 

Is this just me, or you are trying to re-invent "words" function from Prelude?

Matajon
Wow, I didn't know that. But I'm still curious what's wrong with my code :)
Vincent
A: 

Two questions - firstly, why is your output polymorphic? It seems like it's invariably going to be a list of String rather than a list of a. I'm not that familiar with the Haskell type inference internals; try changing the signature to String -> String or String -> [Char] and see if it then works.

Secondly, when you do this it should become clear that your implementation is a little off; even if it did work, it would simply return your original string with all the spaces removed.

If you do want to return a list of strings, you'll need to have a helper function that builds up the current word so far, and then adds that whole word to the output list when the current character is a string. Since it seems like you're doing this to learn (else, just use the Prelude function) I won't give a listing but it shouldn't be too hard to figure out from here.

Andrzej Doyle
+4  A: 

Your type should be String -> [String] or [Char] -> [[Char]].

Your input is a string (a list of chars) your output a list of string (a list of chars of chars).

Your type here means it maps a string to ANY type, this is not true.

Edit: alternatively you can use:

splitBy :: [a] -> a -> [[a]]
splitBy [] sep = []
splitBy (nr1 : rest) if nr1 == sep then splitBy rest sep else nr1 : (splitBy rest sep)

Which is polymorphic and splits a list by any separator. (code not tested though) so splitBy "string of words" ' ' should return ["string","of","words"].

FINALLY, this was annoying and had an obvious and stupid error of [] in lieu of [[]] the functional version is:

splitBy [] sep = [[]]
splitBy (nr1 : rest) sep = if nr1 == sep 
    then [] : splitBy rest sep 
    else (nr1 : head (splitBy rest sep)) : tail (splitBy rest sep)

Such that: splitBy "List of things" ' ' ===> ["list","of","things"]

Lajla
Your first idea helped me, but now I get split chars, no strings... (I updated the question)
Vincent
yeah, I made a mistake I see, let me update my function and this time test it in hugs.
Lajla
...thank you :)
Vincent
The type of splitBy is actually `(Eq a) => [a] -> a -> [[a]]`. It only works for instances of Eq. Not much of a problem in practice, probably — just pointing it out.
Chuck
A: 

Think about what this does:

It iterates through each character in the string.

| nr1 == ' ' = toWords rest

If the character is a space, it skips that character.

| otherwise = [nr1] : toWords rest

Otherwise it creates a string containing only that character, then continues doing so to the rest of the characters in the string.

What you want is to accumulate the characters in the word into a single list rather than creating a new list for each one.

Here's an example of how you could do it:

toWords "" = []
toWords (' ':rest) = toWords rest
toWords text       = let (word, rest) = break (== ' ') text
                     in word : toWords rest
Chuck