Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Haskell have a greedy zip (one preserving all elements)?

Tags:

haskell

I'm not exactly sure if my nomenclature is correct here, but I was wondering if there was a zip function in Haskell that was greedy. This means that if I had

a = [1, 2, 3] b = [4, 5] zip' a b #=> [(Just 1, Just 4),(Just 2, Just 5),(Just 3, Nothing)] 

...where zip' is the greedy zip function, it would return a list of tuples the length of the longer list, and where the longer list has an element, but the shorter list does not Nothing is put in the respective tuple position. I am not asking how to write this, but instead was wondering if this exists as a built-in.

Here is my implementation (which is probably not great)

zip' :: [a] -> [b] -> [(Maybe a, Maybe b)] zip' (a:xs) [] = (Just a, Nothing) : zip' xs [] zip' [] (b:ys) = (Nothing, Just b) : zip' [] ys zip' [] _ = [] zip' (a:xs) (b:ys) = (Just a, Just b) : zip' xs ys 
like image 296
Eli Sadoff Avatar asked Dec 26 '16 21:12

Eli Sadoff


People also ask

What is Haskell zip?

The zip function is used to merge two lists in Haskell. It will merge with the elements present at the same position in both lists.

What is cons operator in Haskell?

The : operator is known as the "cons" operator and is used to prepend a head element to a list. So [] is a list and x:[] is prepending x to the empty list making a the list [x] . If you then cons y:[x] you end up with the list [y, x] which is the same as y:x:[] .

How do you get the first N elements of a list in Haskell?

There is a function in Haskell that takes first n elements of user-supplied list, named take . The syntax is: function-name arg1 arg2 . So, take takes first 1000 elements from an infinite list of numbers from 0 to infinity.


1 Answers

A greedy zip can be neatly expressed through a non-exclusive disjunction type (as opposed to Either, which is an exclusive disjunction). Two popular packages offer that. One is the minimalist, dependency-free data-or:

GHCi> import Data.Or GHCi> :t zipOr zipOr :: [a] -> [b] -> [Or a b] GHCi> zipOr [1, 2, 3] [4, 5] [Both 1 4,Both 2 5,Fst 3] 

The other is these, which comes with lots of bells and whistles:

GHCi> import Data.These  GHCi> import Data.Align GHCi> :t align align :: Align f => f a -> f b -> f (These a b) GHCi> align [1, 2, 3] [4, 5] [These 1 4,These 2 5,This 3] 

I believe Or a b and These a b express your intent better than (Maybe a, Maybe b) (the latter type includes (Nothing, Nothing), which a greedy zip will never produce). Still, you can express your zip' using either zipOrWith from Data.Or...

import Data.Or  zip' :: [a] -> [b] -> [(Maybe a, Maybe b)] zip' = zipOrWith $ \xy -> case xy of     Both x y -> (Just x, Just y)     Fst x -> (Just x, Nothing)     Snd y -> (Nothing, Just y) 

... or alignWith from Data.Align:

import Data.These import Data.Align  zip' :: Align f => f a -> f b -> f (Maybe a, Maybe b) zip' = alignWith $ \xy -> case xy of     These x y -> (Just x, Just y)     This x -> (Just x, Nothing)     That y -> (Nothing, Just y) 

Data.Align, in fact, provides your function under the name of padZip.

like image 112
duplode Avatar answered Oct 17 '22 15:10

duplode