Архивирование с дополнением в Haskell

Пару раз я желал a zip в Haskell, который добавляет дополнение к более короткому списку вместо того, чтобы усечь более длинный. Это достаточно легко записать. (Monoid работы для меня здесь, но Вы могли также просто передать в элементах, которые Вы хотите использовать для дополнения.)

zipPad :: (Monoid a, Monoid b) => [a] -> [b] -> [(a, b)]
zipPad xs [] = zip xs (repeat mempty)
zipPad [] ys = zip (repeat mempty) ys
zipPad (x:xs) (y:ys) = (x, y) : zipPad xs ys

Этот подход становится ужасным при попытке определить zipPad3. Я ввел следующее и затем понял, что, конечно, это не работает:

zipPad3 :: (Monoid a, Monoid b, Monoid c) => [a] -> [b] -> [c] -> [(a, b, c)]
zipPad3 xs [] [] = zip3 xs (repeat mempty) (repeat mempty)
zipPad3 [] ys [] = zip3 (repeat mempty) ys (repeat mempty)
zipPad3 [] [] zs = zip3 (repeat mempty) (repeat mempty) zs
zipPad3 xs ys [] = zip3 xs ys (repeat mempty)
zipPad3 xs [] zs = zip3 xs (repeat mempty) zs
zipPad3 [] ys zs = zip3 (repeat mempty) ys zs
zipPad3 (x:xs) (y:ys) (z:zs) = (x, y, z) : zipPad3 xs ys zs

В этой точке я обманул и просто использовал length выбрать самый длинный список и заполнить другие.

Я пропускаю более изящный способ сделать это или похож на что-то zipPad3 уже определенный где-нибудь?

11
задан Travis Brown 10 June 2010 в 15:40
поделиться

4 ответа

Как насчет пользовательских головных и хвостовых функций (названных next и rest в моем примере ниже) )?

import Data.Monoid

zipPad :: (Monoid a, Monoid b) => [a] -> [b] -> [(a,b)]
zipPad [] [] = []
zipPad xs ys = (next xs, next ys) : zipPad (rest xs) (rest ys)

zipPad3 :: (Monoid a, Monoid b, Monoid c) => [a] -> [b] -> [c] -> [(a,b,c)]
zipPad3 [] [] [] = []
zipPad3 xs ys zs = (next xs, next ys, next zs) : zipPad3 (rest xs) (rest ys) (rest zs)

next :: (Monoid a) => [a] -> a
next [] = mempty
next xs = head xs

rest :: (Monoid a) => [a] -> [a]
rest [] = []
rest xs = tail xs

Тестовый фрагмент:

instance Monoid Int where
  mempty = 0
  mappend = (+)

main = do
  print $ zipPad [1,2,3,4 :: Int] [1,2 :: Int]
  print $ zipPad3 [1,2,3,4 :: Int] [9 :: Int] [1,2 :: Int]

Его вывод:

[(1,1),(2,2),(3,0),(4,0)]
[(1,9,1),(2,0,2),(3,0,0),(4,0,0)]
19
ответ дан 3 December 2019 в 01:38
поделиться

Более простой способ сделать это - использовать Может быть . Я проиллюстрирую с помощью Эдварда более общая формулировка:

import Data.Maybe
import Control.Applicative

zipWithTails l r f as bs = catMaybes . takeWhile isJust $
    zipWith fMaybe (extend as) (extend bs)
  where
    extend xs = map Just xs ++ repeat Nothing
    fMaybe a b = liftA2 f a b <|> fmap l a <|> fmap r b
4
ответ дан 3 December 2019 в 01:38
поделиться

Бывают случаи, когда вы хотите применить другую функцию к хвосту, а не просто указать пустое или ручное обнуление:

zipWithTail :: (a -> a -> a) -> [a] -> [a] -> [a]
zipWithTail f (a:as) (b:bs) = f a b : zipWithTails f as bs
zipWithTail f [] bs = bs
zipWithTail f as _ = as

zipWithTails :: (a -> c) -> (b -> c) -> (a -> b -> c) -> [a] -> [b] -> [c]
zipWithTails l r f (a:as) (b:bs) = f a b : zipWithTails l r f as bs
zipWithTails _ r _ [] bs = fmap r bs
zipWithTails l _ _ as _ = fmap l as

Я использую раньше, когда я делаю что-то вроде zipWithTail (+) и первое, когда мне нужно сделать что-то вроде zipWithTail (* b) (a *) (\ da db -> a * db + b * da) , поскольку первое может быть гораздо более эффективным, чем кормление default в функцию, а последнее - немного.

Однако, если вы просто хотите сделать более сжатую версию того, что у вас есть, вы, вероятно, могли бы обратиться к mapAccumL, но это не яснее, а ++ может быть дорогостоящим.

zipPad as bs = done $ mapAccumL go as bs
    where go (a:as) b = (as,(a,b))
          go [] b = ([],(mempty,b))
          done (cs, both) = both ++ fmap (\x -> (x, mempty)) cs
3
ответ дан 3 December 2019 в 01:38
поделиться

Эта закономерность встречается довольно часто. Решение, которое я узнал от Пола Кьюзано , выглядит следующим образом:

data These a b = This a | That b | These a b

class Align f where
  align :: (These a b -> c) -> f a -> f b -> f c

instance Align [] where
  align f []     []     = []
  align f (x:xs) []     = f (This x)    : align f xs []
  align f []     (y:ys) = f (That y)    : align f [] ys
  align f (x:xs) (y:ys) = f (These x y) : align f xs ys

liftAlign2 f a b = align t
  where t (This l)    = f l b
        t (That r)    = f a r
        t (These l r) = f l r

zipPad a b = liftAlign2 (,) a b

liftAlign3 f a b c xs ys = align t (zipPad a b xs ys)
  where t (This  (x,y))   = f x y c
        t (That  r)       = f a b r
        t (These (x,y) r) = f x y r

zipPad3 a b c = liftAlign3 (,,) a b c

Небольшой тест в ghci:

 *Main> zipPad3 ["foo", "bar", "baz"] [2, 4, 6, 8] [True, False] "" 0 False
 [("foo",2,True),("bar",4,False),("baz",6,False),("",8,False)]
12
ответ дан 3 December 2019 в 01:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: