Haskell: объединяет список кортежей с элементом и списком: [(a, [b])] -> [(a, b)] - PullRequest
3 голосов
/ 07 марта 2019

Я хочу объединить в список кортежей элементов и строк, каждый символ с элементом.

Например: [(True, "xy"), (False, "abc")] ?-> [(True, 'x'), (True, 'y'), (False, 'a') , (Ложь, 'b'), (Ложь, 'c')]

У меня есть решение, но мне интересно, есть ли лучшее:

concatsplit :: [(a,[b])] -> [(a,b)]
concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a

concatsplit' :: (a,[b]) -> [(a,b)]
concatsplit' y = map (\x -> ((fst y),x)) (snd y)

Ответы [ 4 ]

8 голосов
/ 07 марта 2019

Почему бы не простое понимание списка?

Часто понимание списка может выполнять столько же функций, что и функции более высокого порядка, и я думаю, что если вам не нужно изменять данные, а только "распаковать" их, онидовольно ясно.Вот рабочий пример:

concS :: [(a,[b])] -> [(a,b)]
concS ls = [(a,b) | (a,x) <- ls, b <- x]
3 голосов
/ 08 марта 2019

Другие ответы показывают, как сделать это идиоматически с нуля, что мне очень нравится.Также может быть интересно показать, как вы можете отполировать то, что у вас уже есть.Здесь снова напоминание:

concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a
concatsplit' y = map (\x -> ((fst y),x)) (snd y)

Первое, что я бы последовательно изменил, называется «сокращение этажа», и это когда вы превращаете что-то из формы \x -> foo x в просто foo,Мы можем сделать это в аргументе concatMap, чтобы получить

concatsplit a = concatMap concatsplit' a

, а затем снова в аргументе concatsplit, чтобы получить:

concatsplit = concatMap concatsplit'

Глядя на concatsplit',меньше всего мне нравится использование fst и snd вместо сопоставления с образцом.При сопоставлении с образцом это выглядит так:

concatsplit' (a,bs) = map (\x -> (a,x)) bs

Если вы действительно хотите попрактиковаться в сокращении eta, вы можете заметить, что (,) можно применить к префиксу, и измените его на

concatsplit' (a,bs) = map (\x -> (,) a x) bs
                    = map ((,) a) bs

но я думаю, что я так же счастлив, так или иначе.На данный момент, это определение достаточно мало, чтобы я соблазнился вставить его в concatsplit сам, получив:

concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)

Это выглядит как довольно хорошее определение для меня, и я бы остановилсятам.

Вы могут быть прослушены почти-eta-сокращением здесь: это почти правильная форма для отбрасывания bs.Опытный пользователь мог бы продолжить, заметив, что:

uncurry (\a bs -> map ((,) a) bs) = \(a,bs) -> map ((,) a) bs

Следовательно, с помощью некоторого сокращения этажа и других бессмысленных техник мы могли бы перейти следующим образом:

concatsplit = concatMap (uncurry (\a bs -> map ((,) a) bs))
            = concatMap (uncurry (\a -> map ((,) a)))
            = concatMap (uncurry (map . (,)))

Но, лично,Я нахожу это менее читабельным, чем там, где я объявил, что остановлюсь выше, а именно:

concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)
3 голосов
/ 08 марта 2019

sequenceA делает это действительно просто:

concatsplit = concatMap sequenceA

Или обобщите это еще дальше:

concatsplit = (>>= sequenceA)

Детали:

  • sequenceA имеетвведите (Applicative f, Traversable t) => t (f a) -> f (t a).Это означает, что если у вас есть тип с Traversable на «внешней стороне» и Applicative на «внутренней», вы можете вызвать sequenceA для него, чтобы вывернуть его наизнанку, чтобы Applicativeнаходится "снаружи", а Traversable находится внутри ".
  • В этом случае (True, "xy") имеет тип (Bool, [Char]), который переводит в (,) Bool ([] Char).Есть экземпляр из Traversable для ((,) a), и экземпляр из Applicative для [].Таким образом, вы можете вызвать sequenceA для него, и результат будет иметь тип [] ((,) Bool Char) или [(Bool, Char)] с сахаром.
  • Как оказалось, не только sequenceA имеет полезный тип, но он делает именно то, что вам нужно здесь (и оказывается точно эквивалентным concatsplit').
  • concatMap имеет тип Foldable t => (a -> [b]) -> t a -> [b].(>>=) имеет тип Monad m => m a -> (a -> m b) -> m b.Когда они специализируются на списках, они становятся одинаковыми, за исключением того, что их аргументы находятся в обратном порядке.
1 голос
/ 07 марта 2019

Вы также можете использовать экземпляр монады для списков:

concatSplit l = l >>= \(a,x) -> x >>= \b -> return (a,b)

, которые можно упростить до:

concatSplit l = l >>= \(a,x) -> map ((,) a) x

и переформатировать в do нотацию

concatSplit l = do
  (a,x) <- l
  map ((,) a) x
...