Reputation: 75
I have to organize a list of numbers in this data type based on the numbers operator (positive or negative) or if the number is zero. But I'm not sure how to do this.
data Numbers
= Numbers{
pos :: [Int]
, neg :: [Int]
, nul :: [Int]
} deriving Show
That's the function I thought of doing, only it's not complete. How would I do the Add to the _ List part?
numList :: [Int] -> Numbers
numList [] = []
numList ((x:xs)
|x == 0 = add-to-the-nul-list-and : numList xs
|x > 0 = add-to-the-pos-list-and : numList xs
|otherwise = add-to-the-neg-list-and : numList xs
Upvotes: 1
Views: 96
Reputation: 92117
An approach I like is to observe that your type has a monoidal structure. If you write out the instances for that structure, you can use foldMap
to do the list manipulation for you.
instance Monoid Numbers where
mempty = Numbers [] [] []
instance Semigroup Numbers where
(Numbers lpos lneg lnul) <> (Numbers rpos rneg rnul) =
Numbers (lpos <> rpos) (lneg <> rneg) (lnul <> rnul)
numList :: [Int] -> Numbers
numList = foldMap categorize
where categorize x = case compare x 0 of
GT -> Numbers [x] [] []
LT -> Numbers [] [x] []
EQ -> Numbers [] [] [x]
Obviously this is more code than just doing the single task before you, but it's generally helpful to write out instances for classes your type could participate in. Having this Monoid instance will simplify other future tasks as well.
Upvotes: 2
Reputation: 120751
There are multiple ways you can do this:
Bind the recursive result locally, then put the constructor back together with one of the fields modified.
numList (x:xs)
| x==0 = Numbers posxs negxs (x:nulxs)
| x>0 = Numbers (x:posxs) negxs nulxs
| otherwise = Numbers posxs (x:negxs) nulxs
where Numbers posxs negxs nulxs = numList xs
Compute it in a local binding and use record update syntax.
numList (x:xs)
| x==0 = nums_xs{ nul = x : nul nums_xs }
| x>0 = nums_xs{ pos = x : pos nums_xs }
| otherwise = nums_xs{ neg = x : neg nums_xs }
where nums_xs = numList xs
Use lens
es to apply an update to a specific record field. This is most nicely done by modifying the data definition a bit:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Numbers = Numbers{
_pos :: [Int]
, _neg :: [Int]
, _nul :: [Int]
} deriving Show
makeLenses ''Numbers
Then
numList (x:xs)
| x==0 = nums_xs & nul %~ (x:)
| x>0 = nums_xs & pos %~ (x:)
| otherwise = nums_xs & neg %~ (x:)
where nums_xs = numList xs
In this form, you can also remove the code duplications, your entire function can be written
numList [] = Numbers [] [] []
numList (x:xs) = numList xs
& ( case compare x 0 of
EQ -> nul
LT -> neg
GT -> pos
) %~ (x:)
or
numList = foldr categorise $ Numbers [] [] []
where categorise x
| x==0 = nul %~ (x:)
| x>0 = pos %~ (x:)
| otherwise = neg %~ (x:)
Upvotes: 4