Haskell:如何编写将元组列表合并为元组列表的函数?

时间:2018-09-20 05:15:39

标签: list haskell tuples

我正在学习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

1 个答案:

答案 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

如果您想通过直接递归来完成此任务,请查看标准库中的mapfoldr的定义,并尝试将它们手动内联到一个函数中。

另一种改进代码的好方法是将这些元组替换为数据类型,例如:

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 }