splitting strings in haskell programming - haskell

Hi i am trying to do a function input is a list of strings, and the output is again a list of all words occurring in the input.
for example input ["For example,", "love,", "hate."]
output ["For","example","love","hate"]
atm i have this. Any help would be appreciated. Also how can i remove the blank space with just one function and in linear time?
And not using any existing function
split' :: String -> [String]
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

There's a cool one-line to perform what you need
["For example,", "love,", "hate."] >>= words
>>= has type (>>=) :: Monad m => m a -> (a -> m b) -> m b, which takes a function which returns a monadic structure and joins the result into the monadic structure.
If you want to implement words by yourself:
words' xs =
let
waitForBlank (acc, buff) [] = (acc ++ [buff], buff)
waitForBlank (acc, buff) (x:xs) =
if x == ' ' then
waitForBlank (acc ++ [buff], []) xs
else
waitForBlank (acc, buff ++ [x]) xs
in
fst (waitForBlank ([], []) xs)
Or with using (:) and reverse the result (for better performance):
words'' xs =
let
waitForBlank (acc, buff) [] = (reverse (buff : acc), buff)
waitForBlank (acc, buff) (x:xs) =
if x == ' ' then
waitForBlank ((reverse buff) : acc, []) xs
else
waitForBlank (acc, x:buff) xs
in
fst (waitForBlank ([], []) xs)

Related

Is there a way to use "<=" for pattern matching in Haskell?

I have the following code, that drops every nth element in a list.
dropEvery :: [a] -> Int -> [a]
dropEvery xs n = f xs n ++ dropEvery (drop n xs) n
where
f ys 0 = []
f ys 1 = []
f [] m = []
f (y:ys) n = y : (f ys (n-1))
I would like to make it a bit shorter and was wondering if there is a way to use "<=" in pattern matching. I tried doing this using a where clause, which did not work, why?
f ys m = []
where
m <= 1 || ys == []
How can I shirk this redundancy? Is there a nice way to use "less or equal" in pattern matching?
EDIT: I tried this using guards
where
f ys m
| m <= 1 || null ys = []
| otherwise = (head ys) : (f (tail ys) (n-1))
You can work with a guard:
dropEvery :: [a] -> Int -> [a]
dropEvery xs n = f xs n ++ dropEvery (drop n xs) n
where
f ys i | i <= 1 = []
f [] _ = []
f (y:ys) n = y : (f ys (n-1))
If the condition in the guard is satisfied, then that clause "fires" and thus in this case will return an empty list [].
You will however get stuck in an infinite loop, since you write f xs n ++ dropEvery (n xs) n but drop 3 [] will return [], and thus it will keep calling dropEvery with an empty list.
You can make use of recursion where we each time decrement n until it reaches 0, and then we make two hops, so:
dropEvery :: Int -> [a] -> [a]
dropEvery n = go (n-1)
where go _ [] = []
go i (x:xs)
| i <= 0 = go (n-1) xs
| otherwise = x : go (i-1) xs
We can also work with splitAt :: [a] -> ([a], [a]) and with a pattern guard:
dropEvery n [] = []
dropEvery n ds
| (_:ys) <- sb = sa ++ dropEvery n ys
| otherwise = sa
where (sa, sb) = splitAt (n-1) ds

How to split a list of numbers into a set of list with all the numbers in Haskell

How do i split a list in Haskell, for example, "222 33244" into ["222","33","2","444"] only through recursion and fuctions on the prelude?
My current attempt is:
list xs
|length xs == 0 = ""
|otherwise = listSplit xs
listSplit (x:xs)
|x == head xs = x : ListSplitNext x xs
|otherwise = x:[]
listSplitNext a (x:xs)
|a == x = a : listSplitNext x xs
|otherwise = listSplit xs
So since I don't quite understand your approach and ghci lists 18 compile errors in your code, I'm afraid I can't help you with your attempt at a solution.
As pointed out in a comment, a possible solution would be:
listSplit xs = listSplit' [] (filter (`elem` ['0'..'9']) xs)
listSplit' ws (x:xs) = listSplit' (ws ++ [x : takeWhile (==x) xs]) (dropWhile (==x) xs)
listSplit' ws [] = ws
Filter every element of the string that is not a number (Data.Char.isNumber would do this, too, but the premise was to only use Prelude functions) and call listSplit' on the filtered list.
(ws ++ [x : takeWhile (==x) xs]) collects everything in xs until it reaches a letter that does not equal x, wraps this in a list and appends it to ws.
(dropWhile (==x) xs) removes every letter in xs until it reaches a letter that does not equal x.
Finally, the function calls itself with the updated ws and the reduced xs
If there are no more remaining elements, the function returns ws
If your goal is to use very few pre-defined functions this might give you some ideas:
listSplit :: String -> [String]
listSplit xs =
let (as, a) = foldr go ([], []) numbers
in a : as
where
isNumber x = x `elem` ['0'..'9']
numbers = filter isNumber xs
go cur (res, []) = (res, [cur])
go cur (res, lst#(a:_))
| a == cur = (res, a : lst)
| otherwise = (lst : res, [cur])
Of course you can replace foldr with your own recursion as well:
numberSplit :: String -> [String]
numberSplit xs =
let numbers = filter (`elem` ['0'..'9']) xs
in listSplit numbers
listSplit :: Eq a => [a] -> [[a]]
listSplit =
reverse . go [] []
where
go acc as [] = as : acc
go acc [] (x:xs) = go acc [x] xs
go acc as#(a:_) (x:xs)
| a == x = go acc (a : as) xs
| otherwise = go (as : acc) [x] xs
I had a moment to implement this but I think this is what you're looking for.
listSplit s = go filtered
where filtered = [c | c <- s, elem c ['0'..'9']]
go [] = []
go (x:xs) = (x : takeWhile (== x) xs) : (go $ dropWhile (== x) xs)

Haskell - output a type which takes 2 parametes

I'm trying to make an array of my own type letterCount in form of [('letter',occurance),(),...]. How can I make an output of a type which takes 2 parameters. Here's my code:
type LetterCount = (Char,Int)
letterOccur :: Char->[Char] -> Int
letterOccur c [] = 0
letterOccur c (x:xs) = if (c == x) then ((letterOccur c xs) + 1)
else letterOccur c xs
letterStats :: [Char] -> [LetterCount]
letterStats :: [] = []
letterStats (x:xs) = [x,(letterOccur x (x:xs))] ++ letterStats xs
I'm guessing you're trying to do something like this:
letterStats :: [Char] -> [LetterCount]
letterStats :: [] = []
letterStats (x:xs) = (x, (+1) $ letterOccur x xs) : letterStats xs
All you want to do is add +1 as you are not counting x when checking xs for all x occurances. Also what you want is to return a list of the letterCount type which is a Tuple not a list, therefore I changed
[x, letterOccur x xs] ++ letterStats xs
to
(x, (+1) $ letterOccur x xs) : letterStats xs
Although you could also do this:
[(x, (+1) $ letterOccur x xs)] ++ letterStats xs
But is unnecessary.

Reducing this Haskell function

I want to double every second element of a list. Here is the code-
doubleSec n [] = []
doubleSec n (x:xs)
| n==1 = x*2 : doubleSec 0 xs
| otherwise = x : doubleSec 1 xs
doubleSecond xs =
doubleSec 0 xs
How can I compact this logic in a single function?
You can match a pattern on the list like this
doubleSec :: [Int] -> [Int]
doubleSec [] = []
doubleSec [x] = [x]
doubleSec (x : y : xs) = x : 2* y : doubleSec xs
letting you do specific things to the second element
How about this
doubleSecond xs = map (\(x,i) -> if odd i then x*2 else x) (zip xs [0..])
This method will preserve the O(n) running time:
doubleSecond xs =
[ if isOddStep then 2 * x else x |
(isOddStep, x) <- zip (cycle [False, True]) xs ]
An more succinct version by #DavidFletcher:
doubleSecond = zipWith ($) (cycle [id, (2*)])
or:
doubleSecond = zipWith id (cycle [id, (2*)])
as suggested by #Carl.

Simple haskell splitlist

I have the following function which takes a list and returns two sublists split at a given element n. However, I only need to split it in half, with odd length lists having a larger first sublist
splitlist :: [a] -> Int -> ([a],[a])
splitlist [] = ([],[])
splitlist l#(x : xs) n | n > 0 = (x : ys, zs)
| otherwise = (l, [])
where (ys,zs) = splitlist xs (n - 1)
I know I need to change the signature to [a] -> ([a],[a]), but where in the code should I put something like length(xs) so that I don't break recursion?
In a real program you should probably use
splitlist :: [a] -> ([a], [a])
splitlist xs = splitAt ((length xs + 1) `div` 2) xs
(i.e. something along the lines of dreamcrash's answer.)
But if, for learning purposes, you're looking for an explicitly recursive solution, study this:
splitlist :: [a] -> ([a], [a])
splitlist xs = f xs xs where
f (y : ys) (_ : _ : zs) =
let (as, bs) = f ys zs
in (y : as, bs)
f (y : ys) (_ : []) = (y : [], ys)
f ys [] = ([], ys)
You can do it using take and drop:
splitlist :: [a] -> ([a],[a])
splitlist [] = ([],[])
splitlist l = let half = (length(l) +1)`div` 2
in (take half l, drop half l)
or you can take advantage of the function splitAt:
splitlist list = splitAt ((length (list) + 1) `div` 2) list
You can do this by using the take and drop built-in functions. But if you want something that can be done with all self written functions try this:
dropList :: Int -> [Int] -> [Int]
dropList 0 [] = []
dropList 0 (x:xs) = x:xs
dropList y [] = []
dropList y (x:xs) = dropList (y-1) xs
takeList :: Int -> [Int] -> [Int]
takeList 0 [] = []
takeList 0 (x:xs) = []
takeList y [] = []
takeList y (x:xs)
| y <= length (x:xs) = x : takeList (y-1) xs
| otherwise = []
split :: Int -> [Int] -> ([Int],[Int])
split 0 [] = ([],[])
split y [] = ([],[])
split y (x:xs) = (takeList y (x:xs), dropList y (x:xs))
main = do
print (split 4 [1,2,3,4,5,6])

Resources