您的函数combine 被定义为获取单个元素的列表并返回该元素;给定一个包含零个或多个项目的列表,它会如您所观察到的那样引发运行时错误。编译器会在启用警告的情况下警告此类“非详尽模式”,例如-Wall。
如果您有一个值容器,并且您想以某种方式将它们全部组合起来,那么这很好地表明您可以使用折叠。只看这个子列表:
sublist = [("String",1, "String", "DEF", 2.0),("String",4, "String", "DEF", 2.0)]
您可以将这些值与foldr1(或foldl1)组合:
foldr1 :: Foldable t => (a -> a -> a) -> t a -> a
当t = [] 时(a -> a -> a) -> [a] -> a。
combineGroup xs = foldr1 combine xs
where
combine
(_, x, _, _, y) -- Current item in list
(s1, totalX, s2, name, totalY) -- Accumulator for results
= (s1, totalX + x, s2, name, totalY + y)
这假设组永远不会为空;如果可以为空,请使用foldr(或foldl')为累加器提供初始“默认”值:
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
然后,要将这个函数应用于外部列表中的每个组,只需使用map:
map combineGroup groups
如果您想通过直接递归作为学习练习来做到这一点,请查看标准库中map 和foldr 的定义,并尝试手动将它们内联到单个函数中。
另一个改进代码的好方法是将这些元组替换为数据类型,例如:
data Info = Info
{ infoString1, infoString2 :: String
, infoX :: Int
, infoName :: String
, infoY :: Double
} deriving (Show) -- Add other classes like Eq, Ord, &c. as needed.
然后可以使用记录更新更清楚地编写折叠以仅更新您关心的字段,而无需手动探索不相关的字段:
combineGroup xs = foldr1 combine xs
where
combine current acc = acc
{ x = infoX acc + infoX current
, y = infoY acc + infoY current
}
-- OR
{-# LANGUAGE NamedFieldPuns #-}
combineGroup xs = foldr1 combine xs
where
combine Info{ x, y } acc@Info{ x = totalX, y = totalY }
= acc { x = totalX + x, y = totalY + y }