如何压缩不同长度的列表?

问题描述 投票:0回答:6

我怎样才能

zip
两个类似的列表

["Line1","Line2","Line3"]
["Line4","Line5"]

不丢弃第一个列表中的其余元素?

如果可以的话,我想用空列表压缩额外的元素。

haskell
6个回答
16
投票
zipWithPadding :: a -> b -> [a] -> [b] -> [(a,b)]
zipWithPadding a b (x:xs) (y:ys) = (x,y) : zipWithPadding a b xs ys
zipWithPadding a _ []     ys     = zip (repeat a) ys
zipWithPadding _ b xs     []     = zip xs (repeat b)

只要有元素,我们就可以简单地对它们进行压缩。一旦我们用完元素,我们只需使用填充元素的无限列表来压缩剩余列表。

在您的情况下,您可以将其用作

zipWithPadding "" "" ["Line1","Line2","Line3"] ["Line4","Line5"]
-- result: [("Line1","Line4"),("Line2","Line5"),("Line3","")]

10
投票

另一个解决方案是创建一个适用于幺半群的 zip 函数,并用 mempty 填充缺失值:

import Data.Monoid

mzip :: (Monoid a, Monoid b) => [a] -> [b] -> [(a, b)]
mzip (a:as) (b:bs) = (a, b) : mzip as bs
mzip []     (b:bs) = (mempty, b) : mzip [] bs
mzip (a:as) []     = (a, mempty) : mzip as []
mzip _      _      = []

> mzip ["Line1","Line2","Line3"] ["Line4","Line5"]
[("Line1","Line4"),("Line2","Line5"),("Line3","")]

1
投票

Reite 解决方案的另一种实现,使用高阶函数,只是为了好玩。 :) 不过可能会慢一些,因为我猜长度函数需要额外遍历列表。

import Data.Monoid (mempty)

zipPad :: (Monoid a, Monoid b) => [a] -> [b] -> [(a,b)]
zipPad xs ys = take maxLength $ zip (pad xs) (pad ys)
    where
        maxLength = max (length xs) (length ys)
        pad v = v ++ repeat mempty

1
投票

我认为如果你是 Haskell 编程新手,这对你来说会很简单

         zip' :: [String] -> [String] ->[(String,String)]
         zip' [][] = []
         zip' (x:xs)[] = bmi x : zip' xs []
                   where bmi x = (x,"")
         zip' [](x:xs) = bmi x : zip' [] xs
                   where bmi x = ("",x)
         zip' (x:xs) (y:ys) = bmi x y : zip' xs ys
                   where bmi x y = (x,y)    

1
投票

有时我不想填满我的清单。例如,当我只想压缩等长列表时。这是一种通用解决方案,如果一个列表较长,它可能会返回任何额外的值。

zipWithSave :: (a -> b -> c) -> [a] -> [b] -> ([c],Maybe (Either [a] [b]))
zipWithSave f    []     []  = ([],Nothing)
zipWithSave f    []     bs  = ([],Just (Right bs))
zipWithSave f    as     []  = ([],Just (Left as))
zipWithSave f (a:as) (b:bs) = (f a b : cs , sv)
  where (cs, sv) = zipWithSave f as bs

使用

(zps,svs) = zipWithSave f as bs
svs
可以是以下三种情况之一:
Just (Left x)
,其中
as
的剩余物返回为
x
Just (Right x)
,其中返回
bs
的剩余物,或
Nothing
等长列表的情况。

另一种通用目的是为每种情况提供额外的功能。

zipWithOr :: (a -> b -> c) -> (a -> c) -> (b -> c) -> [a] -> [b] -> [c]
zipWithOr _ _  _     []    []   = []
zipWithOr _ _  fb    []     bs  = map fb bs
zipWithOr _ fa _     as     []  = map fa as
zipWithOr f fa fb (a:as) (b:bs) = (f a b) : zipWithOr f fa fb as bs

这只是 Zeta 方法的详细阐述。然后将该函数实现为(使用 {-# LANGUAGE TupleSections #-}):

zipWithPadding a b as bs = zipWithOr (,) (,b) (a,) as bs 

0
投票

我知道这是一个老问题,但为了文档的缘故,一个更简单的解决方案是:

{- | Zip two lists together, padding the shorter list with `Nothing` values so all values
 in either list are conserved.
-}
zipPadded :: forall a b. [a] -> [b] -> [(Maybe a, Maybe b)]
zipPadded as bs =
  zip ((Just <$> as) <> repeat Nothing) ((Just <$> bs) <> repeat Nothing)
    & takeWhile (not . bothNothing)
  where
    bothNothing (Nothing, Nothing) = True
    bothNothing _ = False

这可以由填充默认值等的函数组成

© www.soinside.com 2019 - 2024. All rights reserved.