简体   繁体   English

在折叠中分解类型为种子

[英]Decomposing type as seed in fold

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. 我想计算前n个数的总和,并在每次迭代时保持每个添加数的计数。 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: 我需要在foldl'使用Avg类型的种子,但我需要在聚合器函数内分解它:

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? 给定类型data T = T { a :: Int, b :: Int }并给出类型为T的变量myvar ,如何将其放置为模式匹配而不是其数据构造函数?

In my example the foldl' takes an Avg which is the seed and one element from the list. 在我的例子中, foldl'取一个Avg ,它是seed和列表中的一个元素。

I need (\\x y-> (x sum+y count+1)) instead of (\\x y-> (Avg sum+y count+1)) . 我需要(\\x y-> (x sum+y count+1))而不是(\\x y-> (Avg sum+y count+1))

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) (使用let foo .. in ..也是可能的)

Since data Avg = Avg { sum :: Int, count :: Int } is isomorphic to (Int, Int) , you could also fold with a tuple: 由于data Avg = Avg { sum :: Int, count :: Int }(Int, Int)同构,因此您也可以使用元组折叠:

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包装器:

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). 考虑找一个移动平均线 (Haskell)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM