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
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.
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:[] .
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With