The group
function in Data.List
can group the same element in a list
Input:
import Data.List(group)
group "mississippi time"
Output:
["m","i","ss","i","ss","i","pp","i"," ","t","i","m","e"]
How to modify the group
function to group the different adjacent elements together?
Expect:
["mi","ss","i","ss","i","pp","i time"]
Below is my WRONG solution.
I try to group the same elements and then group the different elements. When grouping different elements, I also check the length of each element. Since after groupSame
, different elements are left alone.
-- |
-- >>> groupSame "mississippi time"
-- ["m","i","ss","i","ss","i","pp","i"," ","t","i","m","e"]
groupSame :: String -> [String]
groupSame [] = []
groupSame (x:xs) = (x : takeWhile (== x) xs) : groupSame (dropWhile (== x) xs)
-- >>> groupDiff ["m","i","ss","i","ss","i","pp","i"," ","t","i","m","e"]
-- ["mi","ss","i","ss","i","pp","i time"]
groupDiff :: [String] -> [String]
groupDiff [] = []
groupDiff [x] = [x]
groupDiff (x:y:xs)
|length x > 1 && length y > 1 = x:y : groupDiff xs
|length x > 1 && length y ==1 = x: groupDiff (y:xs)
|length x ==1 && length y > 1 = x:y: groupDiff xs
|length x ==1 && length y ==1 = (x y): groupDiff xs
The expected output is above the groupDiff
signature.
However I got:
["mi","ss","i","ss","i","pp","i"," t","im","e"]
CodePudding user response:
What about this?
map concat $ groupBy (\x y -> all ((== 1) . length) [x, y]) $ group "mississippi time"
I guess the lambda can be shortened somehow.
I guess (== 1) . length
could be named isSingleton
.
And, it could be implemented as null . tail
to save some parenthesis, fwiw:
map concat $ groupBy (\x y -> all (null . tail) [x, y]) $ group "mississippi time"
With some more playing, you can even get rid of variables x
and y
and write it in point-free style, provided you write an alternative of all
for pairs,
all' p (a, b) = p a && p b
map concat $ groupBy (curry $ all' (null . tail)) $ group "mississippi time"
but this is likely less readable.
You need import Data.List (groupBy)
too.
Somebody else explained to you in a comment why your solution does not work.
My point, however, is that you shouldn't reach for such complicated solutions in the first place.
Hand-made recursion is a powerful tool, but for such a simple tast is just too much powerful; indeed, you get lost into details.
If you know that you just want to group things in some way, then reach out for functions that are for grouping, like group
and groupBy
, and see how you can combine them together to get what you want.
That's how I crafted my solution (which, performance-wise, might be not optimal), by just writing down what I wanted to do,
- group letters by equality (thus obtaining strings of different lengths)
- group together the adjacent strings that have length 1
- concatenate the strings in each of the groups
and then extracting the functions I need from the key parts of my plan:
- group by equality: that's what
group
does - group strings that have length 1:
groupBy
allows grouping given a predicate other than equality,length
can compute the length, and(== 1)
is for checking if a number is 1 - concatenate the strings:
concat
does just that (for any list, not just strings, btw) - in each of the groups: that means I have to
map
on the list of groups.
Given the above, I got here:
map concat $ groupBy (\x y -> doTheyGoTogether) $ group "mississippi time"
where I knew doTheyGoTogether
would use (== 1) . length
somehow. Not a huge leap to understand that (== 1) . length
should be true for all of x
and y
: doTheyGoTogether = all ((== 1) . length)
.
P.S: Hoogle is your friend.