首先,写出类型:
have :: [((Int,Int), [(String,Int)])]
have = [ ((1,1),[("A", 1), ("D", 4), ("E", 5)]), ... ]
want :: [((Int,Int), Int)]
显然,(Int,Int) 在这里并不重要,String 也不是。所以我们的转换函数可以是
give :: [(a, [(b, Int)])] -> [(a, Int)]
此外,a 只是按原样传递,所以有趣的部分将是 [(b, Int)] -> Int。为此,您首先需要扔掉bs
Prelude> map snd [("A", 1), ("D", 4), ("E", 5)]
[1,4,5]
并对结果求和(组合!)
Prelude> sum . map snd $ [("A", 1), ("D", 4), ("E", 5)]
10
所以sum . map snd 是您需要应用于外部元组列表中每个 RHS 的函数。
您如何仅在 RHS 上实际使用它?好吧,一种方法是写一个 lambda
\(x,y) -> (x, f y)
...但实际上有一个标准组合器,称为second
second :: (b -> c) -> (d,b) -> (d,c)
(对元组的snd 元素进行操作,对其应用函数†,将其放回元组中)。
Prelude Control.Arrow> second (sum . map snd) (346, [("A", 1), ("D", 4), ("E", 5)])
(346,10)
剩下要做的就是将整个内容映射到外部列表:
Prelude Control.Arrow> map (second $ sum . map snd) [((1,1),[("A", 1), ("D", 4), ("E", 5)]), ((2,2),[("B", 2)]), ((3,3),[("C",3)])]
[((1,1),10),((2,2),2),((3,3),3)]
或者作为一个定义
give :: Num c => [(a, [(b, c)])] -> [(a, c)]
give = map . second $ sum . map snd
†如果您查看文档,您会发现second 实际上比这更通用:它不仅可以用于函数,还可以用于通用箭头~>,即second :: Arrow (~>) => (b ~> c) -> ((d,b)~>(d,c))。如果这让您感到困惑,请不要担心……在大多数应用程序中,箭头只是普通功能。