2014-01-25 37 views
12

我正在尋找haskell中的函數來壓縮兩個可能長度不同的列表。
我可以找到的所有zip函數都會刪除比其他列表長的所有值。
使用默認值而不是刪除值的Zip?

例如: 在我的練習中,我有兩個示例列表。
如果第一個比第二個短,我必須用0填充。否則,我必須使用1。
我不允許使用任何遞歸。我只需要使用更高階的函數。

有什麼功能可以使用嗎?
到目前爲止,我確實找不到任何解決方案。

回答

6

您可以附加的0或1的inifinte列表,每個列表,然後把你從結果壓縮列表需要數:

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' 
+6

不適用於無限列表。 – augustss

+0

@李:對不起,我的壞,它確實。儘管(儘管其中一個是無限的),但是它已經被提及。下面的答案適用於無限和有限列表。 – Clinton

1

正如你自己說的,標準的zip :: [a] -> [b] -> [(a, b)]下降從要素更長的列表。要修改這個事實,您可以在之前修改您的輸入,並將其輸入zip。首先,您必須找出哪個列表較短(最有可能的是,使用length)。例如,

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

其中x是短xsy的默認值短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-} 
+2

不適用於無限列表。 – augustss

+0

@augustss:對。我應該在我的帖子中提到它。 – chris

0

您不必比較列表長度。嘗試考慮您的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,注意你可以把上面的遞歸變成一個摺疊!

33

這個問題有一些結構,它來了。我將使用這個東西:

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

首先亮相,名單與 - 填充是一個有用的概念,讓我們對他們有一個類型。

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

接着,我記得truncating- 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 "] 
4

這應該做的伎倆:

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

0

這裏是另一種解決方案,那不就無限列表的工作,是前奏的拉鍊功能的簡單升級:

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,謝謝您的啓發解決方案!