【问题标题】:Zip with default value instead of dropping values?使用默认值压缩而不是删除值?
【发布时间】:2014-02-16 10:26:29
【问题描述】:

我在 haskell 中寻找一个函数来压缩两个长度可能不同的列表。
我能找到的所有 zip 函数都只是删除一个列表中比另一个列表长的所有值。

例如: 在我的练习中,我有两个示例列表。
如果第一个比第二个短,我必须使用 0 填充。否则我必须使用 1。
我不允许使用任何递归。我只需要使用高阶函数。

有什么可以使用的功能吗?
到目前为止我真的找不到任何解决方案。

【问题讨论】:

  • @WillNess 而你引用的问题是重复的,因为我的年龄较大
  • 是的,这就是目的。
  • @WillNess 啊,好吧,对不起,我误会你了 :)

标签: list haskell functional-programming higher-order-functions


【解决方案1】:

您可以将 0 或 1 的无限列表附加到每个列表,然后从结果压缩列表中获取您需要的数字:

zipWithDefault :: a -> b -> [a] -> [b] -> [(a,b)]
zipWithDefault da db la lb = let len = max (length la) (length lb)
                                 la' = la ++ (repeat da)
                                 lb' = lb ++ (repeat db)
                             in take len $ zip la' lb'  

【讨论】:

  • @Lee:对不起,我的错,确实如此。虽然它在无限列表上中断(即使一个是无限的),但已经提到过。我下面的答案适用于无限和有限列表。
【解决方案2】:

正如您自己所说,标准 zip :: [a] -> [b] -> [(a, b)] 会从较长的列表中删除元素。要修改这一事实,您可以修改您的输入之前,将其提供给zip。首先,您必须找出哪个列表较短(最有可能使用length)。例如,

zip' x xs y ys | length xs <= length ys  =  ...
               | otherwise               =  ...

其中x 是较短xs 的默认值,y 是较短ys 的默认值。

然后您使用所需的默认元素扩展较短的列表(足以考虑其他列表的附加元素)。一个无需知道较长列表长度的巧妙技巧是使用函数repeat :: a -&gt; [a],它会无限频繁地重复其参数。

zip' x xs y ys | length xs <= length ys = zip {-do something with xs-} ys
               | otherwise              = zip xs {-do something with ys-}

【讨论】:

  • @augustss:对。我应该在我的帖子中提到它。
  • 效率也很低。
  • 如果没记错的话,augustss 删除的评论是“不适用于无限列表”。 length 是罪魁祸首。
【解决方案3】:

您不必比较列表长度。试着把你的 zip 函数想象成一个函数,它只接受一个参数 xs 并返回一个 function,它将接受 ys 并执行所需的 zip。然后,尝试编写一个仅在xs 上递归的recursive 函数,如下所示。

type Result = [Int] -> [(Int,Int)]
myZip :: [Int] -> Result
myZip []     = map (\y -> (0,y)) -- :: Result
myZip (x:xs) = f x (myZip xs)    -- :: Result
   where f x k = ???             -- :: Result

找到 f 后,请注意您可以将上面的递归变成折叠!

【讨论】:

    【解决方案4】:

    这个问题有一些结构,它来了。我将使用这些东西:

    import Control.Applicative
    import Data.Traversable
    import Data.List
    

    首先,lists-with-padding 是一个有用的概念,所以让我们为它们设置一个类型。

    data Padme m = (:-) {padded :: [m], padder :: m} deriving (Show, Eq)
    

    接下来,我记得截断-zip 操作会产生Applicative 实例,在库中为newtype ZipList(非Monad 的流行示例)。 Applicative ZipList 相当于由无穷大和最小值给出的幺半群的装饰。 Padme 具有类似的结构,只是它的底层幺半群是正数(无穷大),使用 1 和最大值。

    instance Applicative Padme where
      pure = ([] :-)
      (fs :- f) <*> (ss :- s) = zapp fs ss :- f s where
        zapp  []        ss        = map f ss
        zapp  fs        []        = map ($ s) fs
        zapp  (f : fs)  (s : ss)  = f s : zapp fs ss
    

    我不得不说出通常的咒语来生成默认的Functor 实例。

    instance Functor Padme where fmap = (<*>) . pure
    

    这样装备好,我们就可以溜走了!例如,接受一个参差不齐的字符串列表并用空格填充它们的函数就变成了单行。

    deggar :: [String] -> [String]
    deggar = transpose . padded . traverse (:- ' ')
    

    看到了吗?

    *Padme> deggar ["om", "mane", "padme", "hum"]
    ["om   ","mane ","padme","hum  "]
    

    【讨论】:

      【解决方案5】:

      这应该可以解决问题:

      import Data.Maybe (fromMaybe)
      
      myZip dx dy xl yl = 
        map (\(x,y) -> (fromMaybe dx x, fromMaybe dy y)) $ 
          takeWhile (/= (Nothing, Nothing)) $ 
          zip ((map Just xl) ++ (repeat Nothing)) ((map Just yl) ++ (repeat Nothing))
      
      main = print $ myZip 0 1 [1..10] [42,43,44]
      

      基本上,将Nothing 的无限列表附加到两个列表的末尾,然后压缩它们,并在两者都是Nothing 时删除结果。然后将Nothings 替换为适当的默认值,同时删除不再需要的Justs。

      【讨论】:

        【解决方案6】:

        这是另一个解决方案,它确实适用于无限列表,并且是 Prelude 的 zip 功能的直接升级:

        zipDefault :: a ->  b -> [a] -> [b] -> [(a,b)]
        zipDefault _da _db []     []     = []
        zipDefault  da  db (a:as) []     = (a,db) : zipDefault da db as []
        zipDefault  da  db []     (b:bs) = (da,b) : zipDefault da db [] bs
        zipDefault  da  db (a:as) (b:bs) = (a,b)  : zipDefault da db as bs
        

        zipDefaultWith :: a -> b -> (a->b->c) -> [a] -> [b] -> [c]
        zipDefaultWith _da _db _f []     []     = []
        zipDefaultWith  da  db  f (a:as) []     = f  a db : zipDefaultWith da db f as []
        zipDefaultWith  da  db  f []     (b:bs) = f da  b : zipDefaultWith da db f [] bs
        zipDefaultWith  da  db  f (a:as) (b:bs) = f  a  b : zipDefaultWith da db f as bs
        

        @pigworker,感谢您的启发性解决方案!

        【讨论】:

          【解决方案7】:

          这可以使用来自 These(“表示具有两种非排他可能性的值”)和Align(“支持采用非均匀形状联合的 zip 操作的函子”)来表示这些库:

          import Data.Align
          import Data.These
          
          zipWithDefault :: Align f => a -> b -> f a -> f b -> f (a, b)
          zipWithDefault da db = alignWith (fromThese da db)
          

          salignData.Align 中的其他专用对齐也值得一看。

          感谢 r/haskell 的 u/WarDaft、u/gallais 和 u/sjakobi 指出这里应该存在这个答案。

          【讨论】:

            【解决方案8】:

            又一个实现:

            zipWithDefault :: a -> b -> (a -> b -> c) -> [a] -> [b] -> [c]
            zipWithDefault dx _  f []     ys     = zipWith f (repeat dx) ys
            zipWithDefault _  dy f xs     []     = zipWith f xs (repeat dy)
            zipWithDefault dx dy f (x:xs) (y:ys) = f x y : zipWithDefault dx dy f xs ys
            

            还有:

            zipDefault :: a -> b -> [a] -> [b] -> [c]
            zipDefault dx dy = zipWithDefault dx dy (,)
            

            【讨论】:

              【解决方案9】:

              没有length,没有计数,没有手工递归,没有合作折叠。 transpose 成功了:

              zipLongest :: a -> b -> [a] -> [b] -> [(a,b)]
              zipLongest x y xs ys = map head . transpose $   -- longest length;
                              [                                 --   view from above:
                                zip  xs 
                                    (ys ++ repeat y)            -- with length of xs
                              , zip (xs ++ repeat x) 
                                     ys                         -- with length of ys
                              ]
              

              transpose 的结果与其输入列表中最长的列表一样长。 map head 获取每个“列”中的第一个元素,这是我们需要的对,以最长的列表为准。


              (update:) 对于任意个列表,有效填充到最大长度——旨在避免其他顺序的潜在二次行为/em>-组合方法--可以遵循相同的想法:

              padAll :: a -> [[a]] -> [[a]]
              padAll x xss = transpose $ 
                 zipWith const
                    (transpose [xs ++ repeat x | xs <- xss])         -- pad all, and cut
                    (takeWhile id . map or . transpose $             --   to the longest list
                       [ (True <$ xs) ++ repeat False | xs <- xss])
              
              > mapM_ print $ padAll '-' ["ommmmmmm", "ommmmmm", "ommmmm", "ommmm", "ommm",
                 "omm", "om", "o"]
              "ommmmmmm"
              "ommmmmm-"
              "ommmmm--"
              "ommmm---"
              "ommm----"
              "omm-----"
              "om------"
              "o-------"
              

              【讨论】:

                猜你喜欢
                • 2018-03-30
                • 2018-05-21
                • 2015-09-28
                • 2011-05-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-06-22
                • 2020-06-13
                相关资源
                最近更新 更多