我正在学习Haskell,不确定如何编写函数来访问列表。
我要修改的数据是:
[[("String",1, "String", "ABC", 2.0)],[("String",1, "String", "DEF", 2.0),("String",4, "String", "DEF", 2.0)]]
当前,基于最后一个字符串(“ ABC”或“ DEF”)将数据分组在一起。我想结合这些信息以获得以下输出:
[("String",1, "String", "ABC", 2.0),("String",5, "String", "DEF", 4.0)]
字符串不变,但int / float被加在一起。
我当前拥有的代码是:
combine :: [(a)] -> (a)
combine [(a)] = (a)
我只是测试一下是否可以访问不同大小的列表,但是它不起作用。当我尝试访问另一组列表时,它给我一个错误。
[("CS",273,"A",1,"Lewis, Buck",1.66),*** Exception: Non-exhaustive patterns in function combineInfo
答案 0 :(得分:4)
您的函数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
当(a -> a -> a) -> [a] -> a
= t
时哪个是[]
。
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 }