2014-12-05 57 views
2

我想知道是否會有一個簡明/單行的方式來執行以下操作:途徑包的列表的(相鄰的)元件成2元組

pack :: [a] -> [(a, a)] 
pack []  = [] 
pack [_]  = [] 
pack (x:y:xs) = (x, y) : pack xs 

哪個是一樣的:

pack' xs = [(x, y) | (x, y, i) <- zip3 xs (tail xs) [0..], even i] 

我對這兩個選項都沒有太大的反應,但我想知道:是否有更簡潔的方式將(,)與其他函數結合使用?

我以爲會有這樣的一種方式,但它沒有我。所以這只是出於好奇。

謝謝!

回答

0

我不知道這是不是一條線,而是:

snd $ foldr (\ x (z, ps) -> maybe (Just x, ps) (\y -> (Nothing, (x, y) : ps) z) (Nothing, []) $ xs 

應該是一樣的功能。

1

注意,對於xs = [x1, x2, ..., xn-1, xn],我們有

init xs = [x1, x2, ... , xn-1] 
tail xs = [x2, x3, ... , xn ] 

導致

zip (init xs) (tail xs) = [(x1, x2), (x2, x3), (x3, x4), ...] 

,我們要的是

pack xs     = [(x1, x2),   (x3, x4), ...] 

這是很容易得到,一旦我們有一個列表口罩

cycle [True, False]  = [ True, False, True, ... ] 

導致一個班輪

pack :: [a] -> [(a, a)] 
pack xs = map snd . filter fst . zip (cycle [True, False]) $ zip (init xs) (tail xs) 
+1

沒了 - 你的版本確實'包[1,2, 3,4] = [(1,2),(2,3),(3,4)]而不是[(1,2),(3,4)]。 – rampion 2014-12-05 02:39:14

+1

你可以縮短這一點,避免空列表問題,使用'pack xs = zip xs(drop 1 xs)'。這仍然有@rampion提到的問題。 – 2014-12-05 02:44:13

+1

好的,我錯過了不應該包含'(2,3)'等的事實。我已經使用相同的推理更新了我的答案。 – Cactus 2014-12-05 02:45:44

4

我們可以很容易分裂列表分爲兩個列表交替元素與此珍聞(due to HaskellWiki

foldr (\a ~(x,y) -> (a:y,x)) ([],[]) 

剩下的工作就是到列表合併與zip

pack :: [a] -> [(a, a)] 
pack = uncurry zip . foldr (\a ~(x,y) -> (a:y,x)) ([],[]) 
+0

哦,這很整齊。我並不完全理解懶惰匹配'〜'的機制,但很明顯foldr本身在做什麼。謝謝! – Dato 2014-12-05 02:59:17

+0

它似乎也沒有懶惰的匹配,維基認爲這是「對提高效率所必需的」。 – Dato 2014-12-05 03:00:04

+2

Dato:要查看何時需要進行懶惰匹配,請嘗試'帶10個(pack [1 ...])'帶和不帶'〜'。惰性匹配可讓您對(可能無限)計算的結果進行模式匹配,而無需等待該計算完成,正如通常在匹配時被迫執行的那樣。 – rampion 2014-12-05 03:03:21

2

另一單行使用LambdaCaseData.List.unfoldr

pack = unfoldr $ \case (x:y:zs) -> Just ((x,y),zs); _ -> Nothing 

我想要的東西偶爾是splits -

splits :: Int -> [a] -> [[a]] 
splits n = unfoldr $ \case [] -> Nothing ; xs -> Just $ splitAt n xs 

而且考慮到,pack變爲:

pack xs = [ (a,b) | [a,b] <- splits 2 xs ] 
相關問題