Haskell: String splitting. Learning algo - haskell

I've seen the other threads about missing split function but I didn't want to peek as I do this for learning purposes and want to figure out myself. So here it is:
split :: Char -> String -> [String]
split c xs | null f = []
| otherwise = f : split c s'
where (f,s) = break (== c) xs
s' | null s = s
| otherwise = tail s
It seems to work fine (please tell me if anything is wrong with it) but when I use a splitting character that is not in the string then the function returns a list with a single element of the original string whereas I want it to return an empty list. I can't figure out how to do it.
Any ideas?.

You can simply write a wrapper function, changing the result of your original one:
split' x xs = go (split x xs) where
go [_] = []
go ys = ys
There are many ways to write a split function, e.g.
split x xs = foldl' go [[]] xs where
go (ys:yss) y | y == x = []:ys:yss
| otherwise = (y:ys):yss
or
import Data.List
split x xs = map tail $ groupBy (const (/=x)) (x:xs)

Related

How to compare multiple strings within in the same list in Haskell

I am trying to write a Haskell function that takes in a list of strings, compares all the strings in the list, and outputs a list of strings that are of the longest length. I want to do this without any predefined functions or imports, I want to try and figure it out all recursively. For example:
longeststrings["meow","cats","dog","woof"] -> ["meow","cats","woof"]
I know it is a silly example, but I think it proves the point.
I want to do something like
longeststrings:: [string] -> [string]
longeststrings [] = []
longeststrings [x:xs] = if (x > xs) x:longeststrings[xs]
But I don't know how to only take the largest size strings out of the list, or remove the smallest ones. I would appreciate any help.
you could trivially keep track of the longest length string and an accumulator of values of that length.
longestStrings :: [String] -> [String]
longestStrings = go [] 0
where
go acc _ [] = acc -- base case
go acc n (x:xs)
| length x > n = go [x] (length x) xs
-- if x is longer than the previously-seen longest string, then set
-- accumulator to the list containing only x, set the longest length
-- to length x, and keep looking
| length x == n = go (x:acc) n xs
-- if x is the same length as the previously-seen longest string, then
-- add it to the accumulator and keep looking
| otherwise = go acc n xs
-- otherwise, ignore it
or, as Davislor rightly mentions in the comments, this can be implemented as a fold by teaching the helper function to determine its own longest length
longestStrings :: [String] -> [String]
longestStrings = foldr f []
where
f x [] = [x]
f x yss#(y:_) =
case compare (length x) (length y) of
GT -> [x]
EQ -> x : yss
LT -> yss
As requested, here’s a version with and without the use of where. I think this is a good demonstration of why the advice not to use where is bad advice. I think the first version is a lot easier to understand.
Keep in mind, functional programming isn’t a monastic vow to forswear certain keywords out of masochism. Nor is it a checklist of fashion tips (where is so last season!). “You should avoid that construct arbitrarily because it’s not the ‘functional’ thing to do” really is not how it works. So you shouldn’t uglify your code for the sake of a tip like that.
It is often a good idea to follow the same coding style as other programmers so they will find it easier to understand you. (For example, Adam Smith was subtly trying to train you that acc is a common name for an accumulator and go a common name for a recursive helper function, and they help other programmers figure out the pattern he’s using.) But in fact Haskell programmers do use where, a lot.
Anyway, the code:
longeststrings :: [String] -> [String]
{- Filters all strings as long as any other in the list. -}
longeststrings = foldr go []
where
go x [] = [x]
go x leaders | newlength > record = [x]
| newlength == record = x:leaders
| otherwise = leaders
where
record = (length . head) leaders
newlength = length x
longestStringsUsingLambda :: [String] -> [String]
longestStringsUsingLambda = foldr
(\x leaders ->
if leaders == [] then [x]
else case compare (length x) (length $ head leaders) of
GT -> [x]
EQ -> x:leaders
LT -> leaders )
[]
main :: IO ()
main = let testcases = [ ["meow","cats","dog","woof"],
["foo","bar","baz"],
["meep","foo","bar","baz","fritz","frotz"]
]
in foldMap print $
map longestStringsUsingLambda testcases
You can try eliminating the let testcases = ... and see if you consider that an improvement.

Remove a Character Sequence From a String

Consider a function, which takes a string and returns a list of all possible cases in which three subsequent 'X's can be removed from the list.
Example:
"ABXXXDGTJXXXDGXF" should become
["ABDGTJXXXDGXF", "ABXXXDGTJDGXF"]
(The order does not matter)
here is a naive implementation:
f :: String -> [String]
f xs = go [] xs [] where
go left (a:b:c:right) acc =
go (left ++ [a]) (b:c:right) y where -- (1)
y = if a == 'X' && b == 'X' && c == 'X'
then (left ++ right) : acc
else acc
go _ _ acc = acc
I think the main problem here is the line marked with (1). I'm constructing the left side of the list by appending to it, which is generally expensive.
Usually something like this can be solved by this pattern:
f [] = []
f (x:xs) = x : f xs
Or more explicitly:
f [] = []
f (x:right) = x : left where
left = f right
Now I'd have the lists right and left in each recursion. However, I need to accumulate them and I could not figure out how to do so here. Or am I on the wrong path?
A solution
Inspired by Gurkenglas' propose, here is a bit more generalized version of it:
import Data.Bool
removeOn :: (String -> Bool) -> Int -> String -> [String]
removeOn onF n xs = go xs where
go xs | length xs >= n =
bool id (right:) (onF mid) $
map (head mid:) $
go (tail xs)
where
(mid, right) = splitAt n xs
go _ = []
removeOn (and . map (=='X')) 3 "ABXXXDGTJXXXDGXF"
--> ["ABDGTJXXXDGXF","ABXXXDGTJDGXF"]
The main idea seems to be the following:
Traverse the list starting from its end. Make use of a 'look-ahead' mechanism which can examine the next n elements of the list (thus it must be checked, if the current list contains that many elements). By this recursive traversal an accumulating list of results is being enhanced in the cases the following elements pass a truth test. In any way those results must be added the current first element of the list because they stem from shorter lists. This can be done blindly, since adding characters to a result string won't change their property of being a match.
f :: String -> [String]
f (a:b:c:right)
= (if a == 'X' && b == 'X' && c == 'X' then (right:) else id)
$ map (a:) $ f (b:c:right)
f _ = []

removing direct duplicates on a list

So i am supposed to remove direct duplicates that are next to each other from a list. For example 1,3,3,3,2,4,4,2,4] = [1,3,2,4,2,4] or [63,65,65,64,65,63,65,65,64,64,65] = [63,65,64,65,63,65,64,65]. My code just removes all duplicates. I think my problem is that i compare with elem and i need a function that only compares with the next element.
module Blueprint where
import Prelude
compress :: [Int] -> [Int]
compress [] = []
compress (x:xs) | x `elem` xs = compress xs
| otherwise = x : compress xs
Right now you are checking if x has a duplicate in the rest of the function. You actually want to check if the next element is a duplicate. You can do this by looking at the first two elements of a list not just the first one.
compress :: [Int] -> [Int]
compress [] = []
compress [x] = [x]
compress (x:x2:xs) | x == x2 = compress (x2:xs)
| otherwise = x : compress (x2:xs)
The group function finds adjacent equal values and groups them together. So your function may be implemented as
compress = map head . group
At first, it may look dangerous to use head; however, a promise of group is that each element it returns is a non-empty list.
I've been getting a lot of bang from this little fold technique lately:
smash :: Eq a => [a] -> [a]
smash xs = foldr go (`seq` []) xs Nothing
where
go x r (Just prev)
| x == prev = r (Just x)
go x r _ = x : r (Just x)

Breaking down a haskell function

I'm reading Real world haskell book again and it's making more sense. I've come accross this function and wanted to know if my interpretation of what it's doing is correct. The function is
oddList :: [Int] -> [Int]
oddList (x:xs) | odd x = x : oddList xs
| otherwise = oddList xs
oddList _ = []
I've read that as
Define the function oddList which accepts a list of ints and returns a list of ints.
Pattern matching: when the parameter is a list.
Take the first item, binding it to x, leaving the remainder elements in xs.
If x is an odd number prepend x to the result of applying oddList to the remaining elements xs and return that result. Repeat...
When x isn't odd, just return the result of applying oddList to xs
In all other cases return an empty list.
1) Is that a suitable/correct way of reading that?
2) Even though I think I understand it, I'm not convinced I've got the (x:xs) bit down. How should that be read, what's it actually doing?
3) Is the |...| otherwise syntax similar/same as the case expr of syntax
1 I'd make only 2 changes to your description:
when the parameter is a nonempty list.
f x is an odd number prepend x to the result of applying oddList to the remaining elements xs and return that result. [delete "Repeat...""]
Note that for the "_", "In all other cases" actually means "When the argument is an empty list", since that is the only other case.
2 The (x:xs) is a pattern that introduces two variables. The pattern matches non empty lists and binds the x variable to the first item (head) of the list and binds xs to the remainder (tail) of the list.
3 Yes. An equivalent way to write the same function is
oddList :: [Int] -> [Int]
oddList ys = case ys of { (x:xs) | odd x -> x : oddList xs ;
(x:xs) | otherwise -> oddList xs ;
_ -> [] }
Note that otherwise is just the same as True, so | otherwise could be omitted here.
You got it right.
The (x:xs) parts says: If the list contains at least one element, bind the first element to x, and the rest of the list to xs
The code could also be written as
oddList :: [Int] -> [Int]
oddList (x:xs) = case (odd x) of
True -> x : oddList xs
False -> oddList xs
oddList _ = []
In this specific case, the guard (|) is just a prettier way to write that down. Note that otherwise is just a synonym for True , which usually makes the code easier to read.
What #DanielWagner is pointing out, is we in some cases, the use of guards allow for some more complex behavior.
Consider this function (which is only relevant for illustrating the principle)
funnyList :: [Int] -> [Int]
funnyList (x1:x2:xs)
| even x1 && even x2 = x1 : funnyList xs
| odd x1 && odd x2 = x2 : funnyList xs
funnyList (x:xs)
| odd x = x : funnyList xs
funnyList _ = []
This function will go though these clauses until one of them is true:
If there are at least two elements (x1 and x2) and they are both even, then the result is:
adding the first element (x1) to the result of processing the rest of the list (not including x1 or x2)
If there are at least one element in the list (x), and it is odd, then the result is:
adding the first element (x) to the result of processing the rest of the list (not including x)
No matter what the list looks like, the result is:
an empty list []
thus funnyList [1,3,4,5] == [1,3] and funnyList [1,2,4,5,6] == [1,2,5]
You should also checkout the free online book Learn You a Haskell for Great Good
You've correctly understood what it does on the low level.
However, with some experience you should be able to interpret it in the "big picture" right away: when you have two cases (x:xs) and _, and xs only turns up again as an argument to the function again, it means this is a list consumer. In fact, such a function is always equivalent to a foldr. Your function has the form
oddList' (x:xs) = g x $ oddList' xs
oddList' [] = q
with
g :: Int -> [Int] -> [Int]
g x qs | odd x = x : qs
| otherwise = qs
q = [] :: [Int]
The definition can thus be compacted to oddList' = foldr g q.
While you may right now not be more comfortable with a fold than with explicit recursion, it's actually much simpler to read once you've seen it a few times.
Actually of course, the example can be done even simpler: oddList'' = filter odd.
Read (x:xs) as: a list that was constructed with an expression of the form (x:xs)
And then, make sure you understand that every non-empty list must have been constructed with the (:) constructor.
This is apparent when you consider that the list type has just 2 constructors: [] construct the empty list, while (a:xs) constructs the list whose head is a and whose tail is xs.
You need also to mentally de-sugar expressions like
[a,b,c] = a : b : c : []
and
"foo" = 'f' : 'o' : 'o' : []
This syntactic sugar is the only difference between lists and other types like Maybe, Either or your own types. For example, when you write
foo (Just x) = ....
foo Nothing = .....
we are also considering the two base cases for Maybe:
it has been constructed with Just
it has been constructed with Nothing

creating infinte reverse add then sort

i'm trying to create an infinte reverse add then sort list in haskell
r = map head (iterate rlist [2..])
rlist (x:xs) = [x : xs | x <- xs , x quick $ p+x ]
where p = reverse x
quick [] = []
quick (x:xs) = quick [u |u <- xs, u < x] ++ [x] ++ quick [u | u <- xs , u >= x]
but its not working, any suggestions?
thanks.
I'm not sure how you expect your code to work (perhaps there was a problem when you posted the code). Namely, part of your list comprehension ... x quick $ p + x makes no sense to me - x isn't a function and it isn't a list so reverse x also makes no sense. For that matter you have shadowed x - notice the x in your list comprehension isn't the same as the x in ratslist (x:xs).
A simple solution does exist using read and show to convert the numbers to lists of digits (well, characters, but it works) and back:
import Data.List
myRats = 1 : map ratify myRats
-- Alternatively: myRats = iterate ratify 1
ratify :: Integer -> Integer
ratify n = sortRat (n + rev n)
where
rev = read . reverse . show
sortRat = read . sort . show
And in GHCi:
*Main Data.List> take 10 myRats
[1,2,4,8,16,77,145,668,1345,6677]
I don't quite get your problem, but according to your example, I'd try
rats x = sort (zipWith (+) x (reverse x))
like in rats [1,4,5] which equals [6,6,8].

Resources