Reputation: 9360
I have the following problem:
I want to calculate the sum of the first n
numbers and keep the count of each added number on every iteration. Therefore i defined a type:
data Avg = Avg { sum :: Int, count :: Int }
I need to use a seed of type Avg
in a foldl'
but i need it decomposed inside the aggregator function:
bang :: [Int] -> IO ()
bang ls@(x:xs) = printAvg $ foldl ' (\x y -> (x sum+y count+1) ) (Avg 0 0) ls
printAvg :: Avg -> IO ()
printAvg av = putStrLn . show (fromIntegral $ sum av / fromIntegral $ count av)
So my question is:
Given a type data T = T { a :: Int, b :: Int }
and given a variable myvar
of type T
, how can I place it for pattern matching instead of its data constructor?
In my example the foldl'
takes an Avg
which is the seed
and one element from the list.
I need (\x y-> (x sum+y count+1))
instead of (\x y-> (Avg sum+y count+1))
.
Upvotes: 1
Views: 84
Reputation: 16105
Since data Avg = Avg { sum :: Int, count :: Int }
is isomorphic to (Int, Int)
, you could also fold with a tuple:
average :: Fractional r => [Int] -> r
average = uncurry (/) . foldr (\x (sum, count) -> (sum+x, count+1)) (0,0)
bang :: [Int] -> IO ()
bang = putStrLn . show . average
And if you want to keep the average running, you could use a newtype
wrapper:
newtype Count = Count (Int, Int)
accumulate :: [Int] -> Count
accumulate = foldr accum (Count (0, 0))
where
accum :: Int -> Count -> Count
accum x (Count (sum, count)) = Count (sum+x, count+1)
average :: Fractional r => Count -> r
average (Count (x, y)) = x / y
bang :: [Int] -> IO ()
bang = putStrLn . show . average . accumulate
You might risk overflows in both cases.
Consider finding a moving average (Haskell).
Upvotes: 1
Reputation: 116139
A few possible solutions:
(\ (Avg s c) y -> Avg (s + y) (c + 1))
-- equivalent to the longer
(\ x y -> case x of Avg s c -> Avg (s + y) (c + 1))
-- mentioning the fields name explicitly
(\ Avg{sum=s, count=c} y -> Avg (s + y) (c + 1))
-- using the RecordWildCards extension
(\ Avg{..} y -> Avg (sum + y) (count + 1))
-- using the two projections
(\ x y -> Avg (sum x + y) (count x + 1))
or even, adapting your code
bang::[Int]->IO()
bang ls@(x:xs) = printAvg $ foldl' foo (Avg 0 0) ls
where
foo (Avg s c) y = Avg (s + y) (c+ 1)
(using let foo .. in ..
is also possible)
Upvotes: 2