Carlos Melo
Carlos Melo

Reputation: 75

How do I handle this data type

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

Answers (2)

amalloy
amalloy

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

leftaroundabout
leftaroundabout

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 lenses 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

Related Questions