代码之家  ›  专栏  ›  技术社区  ›  Travis Brown

Haskell填充拉链

  •  11
  • Travis Brown  · 技术社区  · 16 年前

    有几次我发现自己想要一个 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 选择最长的列表并填充其他列表。

    是我忽略了一个更优雅的方式来做这件事,还是类似的 齐帕德3 已经在某个地方定义了?

    4 回复  |  直到 16 年前
        1
  •  19
  •   sastanin    16 年前

    head tail 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)]
    
        2
  •  12
  •   Apocalisp    7 年前

    这种模式经常出现。我从中学到的解决方法 Paul Chiusano

    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)]
    
        3
  •  4
  •   Yitz    16 年前

    更简单的方法是 Maybe

    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
  •   Edward Kmett    16 年前

    有时,您希望能够将不同的功能应用于任一尾部,而不仅仅是供给 mempty 或手动调零:

    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) 因为前者比将默认值输入函数要有效得多,而后者则稍微有效一点。

    但是,如果您只是想使现有的版本更简洁,那么您可以使用mapacuml,但它并不是更清晰,而且++可能很昂贵。

    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
    
    推荐文章