使用Haskell中的map函数将元组列表转换为列表

6
我希望将元组列表:[(2,2,2),(3,3,3),(4,4,4),(5,5,5)]转换为列表:[2,2,2,3,3,3,4,4,4,5,5,5]。请使用以下代码实现:
map (\(a,b,c,d)->a:b:c:d) listOfTuples

但是会出现错误。

Prelude> map (\(a,b,c)->a:b:c) [(1,2,3), (5,6,7)]

<interactive>:1:37:
    No instance for (Num [t])
      arising from the literal `7' at <interactive>:1:37
    Possible fix: add an instance declaration for (Num [t])
    In the expression: 7
    In the expression: (5, 6, 7)
    In the second argument of `map', namely `[(1, 2, 3), (5, 6, 7)]'

Prelude>

我该如何使用 lambda?为什么我的代码不能正常工作?
2个回答

13
a:b:c:d是无效的,因为它是a : (b : (c : d))。在右侧,(:)接受一个列表,在左侧添加一个元素,但d是另一个元素,不是(必须)一个列表。你可能意思是a:b:c:d:[],这与[a,b,c,d]相同。
如果你进行这个更改,代码将运行,并生成[[2,2,2], [3,3,3], [4,4,4], [5,5,5]],因为map将函数应用于列表的每个元素,并使用其结果作为新元素;也就是说,不能改变列表的结构,只能独立地更改每个元素。如果要对其进行平坦化处理,需要使用concat(其类型为[[a]] -> [a],并展平列表中的列表)。
concat (map (\(a,b,c,d) -> [a,b,c,d]) listOfTuples)

然而,有一种现成的组合方式是concatmap,更符合惯用法:

concatMap (\(a,b,c,d) -> [a,b,c,d]) listOfTuples

但是我能只用map做到吗?我对concatMap的算法难度很感兴趣。 - overwriter
5
不行,你不能只用 map 来实现,因为输入列表中的每个元素只能映射到输出列表中的一个元素。 - hammar
11
或者:foldr (\(a,b,c) l -> a:b:c:l) [] [(1,2,3),(4,5,6)]这段代码的作用是将一个包含两个三元组的列表转换为一个列表。在转换过程中,每个三元组 (a,b,c) 被展开为 a,b,c 以及之前已经处理好的列表 l,然后将它们依次添加到新列表的头部。最终的结果会是 [1,2,3,4,5,6]。其中 foldr 是一个高阶函数,它接受一个函数作为参数,并按照列表的从右向左的顺序对其进行折叠操作。 - Riccardo T.
1
@amindfv:感谢惰性,concatMap f xs 只会遍历列表一次;同样适用于 concat (map f xs) - ehird
3
由于列表单子的实现方式,这使得以下操作成为可能:[(a,b,c),(d,e,f)] >>= \(x,y,z) -> [x,y,z] - Sarah
显示剩余2条评论

4
这也可以用列表推导式更简洁地完成。
假设:
let listOfTuples = [(2,2,2), (3,3,3), (4,4,4), (5,5,5)]

然后是这个列表推导式:
concat [[a,b,c] | (a, b, c) <- listOfTuples]

产生:
[2,2,2,3,3,3,4,4,4,5,5,5]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接