Reputation: 23
Convert string to multi set as the example below:
"bacaba" --> [(b,2),(a,3),(c,1)]
type MSet a = [(a,Int)]
convert :: Eq a => [a] -> MSet
what is wrong with my code and what is the better way to do it? ty
convert :: Eq a => [a] -> MSet a
convert [] = []
convert (x:xs) = ((x,1+count x xs) : converte xs)
where count x [] = 0
count x (y:ys) = if (x == y) then 1 + count x ys else count x ys
Upvotes: 1
Views: 400
Reputation: 77951
what is the better way to do it?
Your code performs O(n^2)
; If the type is an instance of Ord
type class (as in your example), using Data.Map
you may get an O(n log n)
performance:
import Data.Map (toList, fromListWith)
convert :: (Ord a) => [a] -> [(a, Int)]
convert xs = toList . fromListWith (+) . zip xs $ repeat 1
This would result in the right counts but the list would be sorted by the keys:
\> convert "bacaba"
[('a',3),('b',2),('c',1)]
If you need to preserve the order, then
import qualified Data.Map as M
import Data.Map (delete, fromListWith)
convert :: (Ord a) => [a] -> [(a, Int)]
convert xs = foldr go (const []) xs . fromListWith (+) . zip xs $ repeat 1
where
go x f cnt = case M.lookup x cnt of
Just i -> (x, i): f (x `delete` cnt)
Nothing -> f cnt
which would output:
\> convert "bacaba"
[('b',2),('a',3),('c',1)]
Upvotes: 1
Reputation: 48654
You are almost there.
The problem is with your recursive call to the convert
function. Since you have already computed the number of characters for a particular character, you don't need to calculate them again. Just use a filter
function to remove that character out while calling to convert
:
convert :: Eq a => [a] -> MSet a
convert [] = []
convert (x:xs) = (x,1+count x xs) : convert (filter (\y -> y /= x) xs)
where count x [] = 0
count x (y:ys) = if (x == y) then 1 + count x ys else count x ys
Or written more concisely:
convert :: Eq a => [a] -> MSet a
convert [] = []
convert (x:xs) = (x,1+count x xs) : convert (filter (/= x) xs)
where count x [] = 0
count x (y:ys) = if (x == y) then 1 + count x ys else count x ys
Demo in ghci:
ghci| > convert "bacaba"
[('b',2),('a',3),('c',1)]
Upvotes: 1