我有以下问题:
我想计算前n
个数字的总和,并在每次迭代中保留每个加数的计数。因此,我定义了一个类型:
data Avg = Avg { sum :: Int, count :: Int }
我需要在Avg
中使用类型为foldl'
的种子,但是我需要在聚合函数内部对其进行分解:
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)
所以我的问题是:
给出类型data T = T { a :: Int, b :: Int }
并给定类型myvar
的变量T
,我该如何放置它以进行模式匹配而不是其数据构造函数?
在我的示例中,foldl'
包含一个Avg
和一个seed
以及列表中的一个元素。
我需要(\x y-> (x sum+y count+1))
而不是(\x y-> (Avg sum+y count+1))
。
答案 0 :(得分:2)
一些可能的解决方案:
protected $fields = [
'type' => [
'type' => 'anomaly.field_type.select',
'config' => [
'options' => ['foo' => 'Foo', 'bar' => 'Bar'],
'mode' => 'dropdown'
]
]
];
甚至修改您的代码
(\ (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))
(也可以使用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)
)
答案 1 :(得分:1)
由于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
如果要保持平均运行,可以使用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
在这两种情况下,您都可能会有溢出的风险。
考虑找到一个moving average(哈斯克尔)。