Список пар в пару списков Haskell - PullRequest
0 голосов
/ 13 февраля 2019

В основном у меня есть это упражнение: используя списочные выражения, напишите полиморфную функцию:

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]))

Может кто-нибудь объяснить, почему мое решение не работает?Спасибо!

1 Ответ

0 голосов
/ 13 февраля 2019

Понятие списка, например:

[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]), с исходным кодом .

...