【问题标题】:How to zip lists with different length?如何压缩不同长度的列表?
【发布时间】:2014-03-14 10:54:19
【问题描述】:

我怎样才能zip两个列表喜欢

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

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

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

【问题讨论】:

  • 好吧,如果你不丢弃它们,你还能做什么?填充较短的列表?如果有,价值是多少?
  • 我编辑我的问题。会很好,如果在那之后zip会有像("string",[])这样的元素
  • zip list_a $ list_b ++ repeat ""

标签: haskell


【解决方案1】:
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","")]

【讨论】:

  • 想看memptyMonoid的人注意:zipWithPadding mempty mempty会给你(Monoid a, Monoid b) => [a] -> [b] -> [(a,b)]
【解决方案2】:

另一种解决方案是制作一个适用于幺半群的 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","")]

【讨论】:

  • @Riccardo 我不会说它更好,接受的答案将在您不一定有Monoid 的情况下起作用,并且可能希望对不同的拉链使用不同的默认值。该数据可能对<> 没有很好的意义,但仍然有一个健全的mempty(或多个健全的默认值)。 Haskell 中的最佳实践说尽可能使用限制最少的类型,使用 Monoid 会过度限制此功能,因为未使用 <>。此外,mzip 可以定义为 zipWithPadding,只需将 mempty 传递给两个默认值。
  • @Riccardo 我个人认为如果标准库定义了class Default m where mempty :: mclass Default m => Monoid m where mappend :: m -> m -> m 会很有用。
  • @Riccardo:mzip = zipWithPadding mempty mempty。仅仅因为某些东西使用了Monoid 并不能使它变得更好或更强大;)。
  • 我看到我的评论太短了,不会被误解,让我澄清一下:) 我同意zipWithPadding 解决方案更通用的事实,这一点毫无疑问。但问题是关于字符串列表的,因为 OP 明确要求使用"" 填充。在这方面,涉及 monoids 的解决方案不仅在 OP 方面足够通用,而且比必须指定两个不会改变的默认参数更简单。这就是我最喜欢mempty 解决方案的原因。
  • 看起来有人已经在data-default 中实现了Default 类。您可以将mzip 替换为签名(Default a, Default b) => [a] -> [b] -> [(a, b)],并将mempty 替换为def。它已经有许多常见类型的实例,所以如果我已经依赖于那个包,我会选择在这种情况下。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    如果你是 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)    
    

    【讨论】:

      【解决方案5】:

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

      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 bssvs 可以是以下三种情况之一:Just (Left x) 其中as 的剩余部分返回为xJust (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 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-05
        • 2014-05-23
        • 1970-01-01
        • 1970-01-01
        • 2013-11-10
        • 1970-01-01
        相关资源
        最近更新 更多