Reputation: 101
I know Haskell doesn't have loops, so I can't do that. I also know that recursion is "helpful" here, but that's about all I'm aware of. So far, I've gotten a basic type signature, which is
toSplit :: String -> [String]
Basically, it changes from a string into a list of words...
Thanks!
P.S. I want to use the takeWhile
and dropWhile
functions... not a library...
Upvotes: 2
Views: 4495
Reputation: 675
If you want to implement that yourself, first thing you need to do is find the first word. You can do that with:
takeWhile (/=' ') s
If the string starts with delimiter characters you need to trim those first. We can do that with dropWhile
.
takeWhile (/=' ') $ dropWhile (==' ') s
Now we have the first word, but we also need the rest of the string starting after the first word, on which we will recurse. We can use splitAt
:
(_, rest) = splitAt (length word) s
And then we recurse with the rest
of the string and cons the first word onto the result of that recursion giving us a list of all words.
And we need to define the base case, the result for the empty string which will terminate the recursion once there are no more characters.
toSplit :: String -> [String]
toSplit "" = []
toSplit s =
let word = takeWhile (/=' ') $ dropWhile (==' ') s
(_, rest) = splitAt (length word) s
in word : toSplit (dropWhile (==' ') rest)
Edit: There is a bug in the code above and it's not handling an edge case properly.
The bug is that it's calling splitAt on the original s
but this gives a wrong result if s
has leading spaces:
*Main> toSplit " foo"
["foo","o"]
This should fix the bug:
let trimmed = dropWhile (==' ') s
word = takeWhile (/=' ') trimmed
(_, rest) = splitAt (length word) trimmed
That leaves one edge case:
*Main> toSplit " "
[""]
One possible solution is to use a helper function:
toSplit :: String -> [String]
toSplit = splitWords . dropWhile (==' ')
where
splitWords "" = []
splitWords s =
let word = takeWhile (/=' ') s
(_, rest) = splitAt (length word) s
in word : splitWords (dropWhile (==' ') rest)
Upvotes: 5
Reputation: 2066
I know Haskell doesn't have loops, so I can't do that. I also know that recursion is "helpful" here...
Yes, Haskell use recursion rather then for
, while
loop structure that in imperative language.
In stead of write recursive function by myself, it is more common use map
, foldr (foldl)
, unfoldr
or etc recursively traverse the list. The advantage of these functions is that you can concentrate what want to do in step
function. Like your question, is suitable use unfoldr
to accomplish it.
Here is an example with takeWhile
and dropWhile
:
import Data.Char (isSpace)
import Data.List (unfoldr)
toSplit::String->[String]
toSplit = unfoldr step
where step [] = Nothing
step xs = Just (takeWhile (not . isSpace) xs,
(dropWhile isSpace . dropWhile (not . isSpace)) xs)
But a little bit verbose, use break
function may be more readable like:
toSplit' = unfoldr step
where step [] = Nothing
step xs = let (word, rest) = break isSpace xs
in Just (word, dropWhile isSpace rest)
Upvotes: 1