对列表成对列表Haskell

Nic*_*kor 0 polymorphism haskell tuples list-comprehension

基本上我有这个练习:使用列表推导,写一个多态函数:

split :: [(a, b)] -> ([a], [b])
Run Code Online (Sandbox Code Playgroud)

它将一对(任何类型的)列表转换为一对列表.例如,

split [(1, 'a'), (2, 'b'), (3, 'c')] = ([1, 2, 3], "abc")
Run Code Online (Sandbox Code Playgroud)

这是我编写函数的方式,但它不起作用:

split :: [(a, b)] -> ([a], [b])
split listOfPairs = (([a | a <- listOfPairs]), ([b | b <- listOfPairs]))
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么我的解决方案不起作用?谢谢!

Wil*_*sem 6

列表理解如:

[a | a <- listOfPairs]
Run Code Online (Sandbox Code Playgroud)

实际上只不过是列表的身份操作.它将产生与您提供的列表相同的列表,因为您基本上迭代listOfPairs,并且对于每次迭代,您都会产生元素a.

哈斯克尔不执行隐式转换,因此它并不会从类型派生aa <- listOfPairs那么只能是第一要素.即使这是可能的,但无论如何它可能不是一个好主意,因为它会使语言更加"不稳定",因为类型的微小变化可能会对语义产生重大影响.

为了获得元组的第一个元素,您需要使用模式匹配,如:

[a | (a, _) <- listOfPairs]
Run Code Online (Sandbox Code Playgroud)

这里我们因此模式匹配元组的第一个元素a,而对于第二个元素,我们因此使用:

[b | (_, b) <- listOfPairs]
Run Code Online (Sandbox Code Playgroud)

因此,我们可以将此作为:

split:: [(a,b)] -> ([a],[b])
split listOfPairs = ([a | (a, _) <- listOfPairs], [b | (_, b) <- listOfPairs])
Run Code Online (Sandbox Code Playgroud)

或者我们可以使用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)
Run Code Online (Sandbox Code Playgroud)

但是上面仍然有一个问题:这里我们在同一个列表上独立迭代两次.我们可以通过使用递归来省略,例如:

split:: [(a,b)] -> ([a],[b])
split [] = []
split ((a, b):xs) = (a:as, b:bs)
    where (as, bs) = split xs
Run Code Online (Sandbox Code Playgroud)

或者我们可以使用一个foldr函数:

split :: Foldable f => f (a,b) -> ([a],[b])
split = foldr (\(a,b) (as,bs) -> (a:as,b:bs)) ([],[])
Run Code Online (Sandbox Code Playgroud)

已经有一个Haskell函数完全符合您的要求:unzip :: [(a, b)] -> ([a], [b])使用源代码.