使用默认值而不是丢弃值来压缩?

时间:2014-01-25 10:33:30

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

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

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

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

9 个答案:

答案 0 :(得分:38)

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

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

首先,list-with-padding是一个有用的概念,所以让我们有一个类型。

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

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

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  "]

答案 1 :(得分: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指出此答案应该存在。

答案 2 :(得分:6)

您可以在每个列表中附加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'  

答案 3 :(得分:6)

这应该可以解决问题:

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时删除结果。然后将Nothing替换为相应的默认值,在您使用时删除不再需要的Just

答案 4 :(得分:2)

您不必比较列表长度。尝试将您的zip函数视为只接受一个参数xs并返回函数的函数,该函数将ys并执行所需的zip。然后,尝试编写递归函数,该函数仅在xs上进行递归,如下所示。

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之后,请注意您可以将上面的递归变成折叠!

答案 5 :(得分:1)

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

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

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

然后使用所需的默认元素扩展较短的列表(足以考虑其他列表的其他元素)。这样做的巧妙技巧是在不必知道较长列表的长度的情况下使用函数repeat :: a -> [a]来无限地重复其参数。

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

答案 6 :(得分:1)

这是另一个解决方案,可以在无限列表上工作,并且是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 :(得分:0)

另一个实现方式:

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 (,)

答案 8 :(得分:0)

没有position: center, background: cover,没有计数,没有手工制作的递归,没有合作的折叠。 length可以达到目的:

transpose

zipLongest :: a -> b -> [a] -> [b] -> [(a,b)] zipLongest dx dy xs ys = map head . transpose $ -- longest length; [ -- view from above zip xs (ys ++ repeat dy) -- with length of xs , zip (xs ++ repeat dx) ys -- with length of ys ] 的结果与它的输入列表列表中最长的列表一样长。 transpose采用每个“列”中的第一个元素,这是我们需要的对,以最长的列表为准。


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

map head