基本上我有这个练习: 使用列表推导,编写一个多态函数:
split :: [(a, b)] -> ([a], [b])
将(任何类型的)对列表转换为一对列表。例如,
split [(1, 'a'), (2, 'b'), (3, 'c')] = ([1, 2, 3], "abc")
这是我编写函数的方式,但是它不起作用:
split :: [(a, b)] -> ([a], [b])
split listOfPairs = (([a | a <- listOfPairs]), ([b | b <- listOfPairs]))
有人可以解释为什么我的解决方案不起作用吗?谢谢!
答案 0 :(得分:6)
类似列表的理解
[a | a <- listOfPairs]
实际上,仅仅是列表的 identity操作。它将产生与您提供的列表相同的列表,因为您基本上遍历了listOfPairs
,并且对于每次迭代,您都会生成元素a
。
Haskell不执行隐式转换,因此它 not 不从a
中a <- listOfPairs
的类型派生而来,只能是第一个元素。即使有可能,反正也不是一个好主意,因为这会使语言更加“不稳定”,因为类型的微小变化可能会对语义产生重大影响。
为了获得元组的第一个元素,您需要使用模式匹配,例如:
[a | (a, _) <- listOfPairs]
在此,我们将模式的元组的第一个元素与a
进行匹配,对于第二个元素,我们将使用:
[b | (_, b) <- listOfPairs]
因此,我们可以这样实现:
split:: [(a,b)] -> ([a],[b])
split listOfPairs = ([a | (a, _) <- listOfPairs], [b | (_, b) <- listOfPairs])
或者我们可以使用map :: (a -> b) -> [a] -> [b]
,fst :: (a, b) -> a
和snd :: (a, b) -> b
:
split:: [(a,b)] -> ([a],[b])
split listOfPairs = (map fst listOfPairs, map snd listOfPairs)
但是上面仍然有一个问题:在这里,我们在同一列表上独立地迭代两次。我们可以通过使用递归来忽略它,例如:
split:: [(a,b)] -> ([a],[b])
split [] = []
split ((a, b):xs) = (a:as, b:bs)
where (as, bs) = split xs
或者我们可以使用foldr
函数:
split :: Foldable f => f (a,b) -> ([a],[b])
split = foldr (\(a,b) (as,bs) -> (a:as,b:bs)) ([],[])
已经有一个Haskell函数可以完全满足您的需求:unzip :: [(a, b)] -> ([a], [b])
和source code。