从记录列表生成小计

时间:2013-09-23 13:27:50

标签: haskell

我从网络服务获取记录列表并根据该数据生成报告。我目前正在使用与此类似的代码:

let sorted = sortBy (comparing field1 <> comparing field2) records
let grouped = groupBy cmp sorted
              where cmp x y =    (field1 x) == (field1 y)
                              && (field2 x) == (field2 y)
let subtotals = map subtot grouped
                where subtot = foldl1 recsum
                      recsum x y = x { field3 = (field3 x) + (field3 y) }

有没有更简洁的方法来实现这一目标?它有点乱,特别是在按多个字段分组时。即使只为sort + group提供单一功能也会有所帮助。我想过编写一个函数来获取像

这样的字段解构器列表
sortAndGroup :: Ord v => [r -> v] -> [r] -> [[r]]

但是当字段具有不同类型时,这将不起作用。有什么想法吗?

1 个答案:

答案 0 :(得分:7)

不是给sortAndGroup一个字段列表,而是让它给它一个函数,其结果将包含那些字段的元组(或者我们需要的任何其他元素):

import Control.Arrow ((&&&))
import Data.Function (on)
import Data.Ord (comparing)
import Data.List (groupBy, sortBy)

sortAndGroup :: (Ord v) => (r -> v) -> [r] -> [[r]]
sortAndGroup cf = groupBy (on (==) cf) . sortBy (comparing cf)

现在,我们可以根据一组字段轻松地对数据类型进行排序和分组:

data Foo = Foo { field1 :: Int, field2 :: Int, field3 :: Int }
  deriving (Show, Eq, Ord)

bar :: [Foo] -> [[Foo]]
bar = sortAndGroup (\f -> (field1 f, field2 f))

此外,我们可以使用(&&&)中的Control.Arrow组合记录访问者,专门针对->Arrow的一个实例):

-- Specialized for ->
(&&&) ::: (b -> c) -> (b -> c') -> (b -> (c, c'))

所以bar可缩短为

bar' :: [Foo] -> [[Foo]]
bar' = sortAndGroup (field1 &&& field2)