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)
有人可以解释为什么我的解决方案不起作用?谢谢!
列表理解如:
[a | a <- listOfPairs]Run Code Online (Sandbox Code Playgroud)
实际上只不过是列表的身份操作.它将产生与您提供的列表相同的列表,因为您基本上迭代listOfPairs,并且对于每次迭代,您都会产生元素a.
哈斯克尔不执行隐式转换,因此它并不会从类型派生a你a <- 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 xsRun 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])使用源代码.