Понятие списка, например:
[a | a <- listOfPairs]
, на самом деле является не чем иным, как идентификационной операцией для списков.Он выдаст тот же список, что и предоставленный вами, так как вы в основном итерируете по listOfPairs
, и для каждой итерации вы получаете элемент a
.
Haskell не выполняет неявный преобразования, так что не происходит от типов, которые a
в вашем a <- listOfPairs
могут быть только первым элементом.Даже если бы это было возможно, это, вероятно, было бы не хорошей идеей в любом случае, поскольку это сделало бы язык более "нестабильным" в том смысле, что небольшое изменение типов могло бы оказать существенное влияние на семантику.
Чтобы получить первый элемент кортежа, вам нужно использовать сопоставление с шаблоном , например:
[a | <b>(a, _)</b> <- listOfPairs]
здесь мы, таким образом, сопоставим шаблон с первым элементомкортеж с a
, а для второго мы, таким образом, используем:
[b | <b>(_, b)</b> <- listOfPairs]
Таким образом, мы можем реализовать это как:
split:: [(a,b)] -> ([a],[b])
split listOfPairs = ([a | <b>(a, _)</b> <- listOfPairs], [b | <b>(_, 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])
, с исходным кодом .