In python zip
function accepts arbitrary number of lists and zips them together.
>>> l1 = [1,2,3]
>>> l2 = [5,6,7]
>>> l3 = [7,4,8]
>>> zip(l1,l2,l3)
[(1, 5, 7), (2, 6, 4), (3, 7, 8)]
>>>
How can I zip
together multiple lists in haskell?
In python zip
function accepts arbitrary number of lists and zips them together.
>>> l1 = [1,2,3]
>>> l2 = [5,6,7]
>>> l3 = [7,4,8]
>>> zip(l1,l2,l3)
[(1, 5, 7), (2, 6, 4), (3, 7, 8)]
>>>
How can I zip
together multiple lists in haskell?
Looks like there is also a zip3
(doc) and a zip4
(doc) function in Haskell. But the zipn seems to be complicated because of the strong type system. Here is a good discussion I've found during my research.
You can transpose a list of lists:
>>> import Data.List
>>> transpose [l1,l2,l3]
[[1,5,7],[2,6,4],[3,7,8]]
It's non-trivial, but it is doable. See this blog post. I dont know whether this made into some library.
Here is another version, which is simplier. This one could actually be cut-n-pasted here:
{-# LANGUAGE MultiParamTypeClasses
, FunctionalDependencies
, FlexibleInstances
, UndecidableInstances
#-}
-- |
-- Module : Data.List.ZipWithN
-- Copyright : Copyright (c) 2009 wren ng thornton
-- License : BSD3
-- Maintainer : [email protected]
-- Stability : experimental
-- Portability : non-portable (MPTCs, FunDeps,...)
--
-- Provides a polyvariadic 'map'/'zipWith' like the @map@ in Scheme.
-- For more details on this style of type hackery, see:
--
-- * Chung-chieh Shan, /A polyvariadic function of a non-regular/
-- /type (Int->)^N ([]^N e)->.../
-- <http://okmij.org/ftp/Haskell/polyvariadic.html#polyvartype-fn>
----------------------------------------------------------------
module Data.List.ZipWithN (ZipWithN(), zipWithN) where
-- | This class provides the necessary polymorphism. It is only
-- exported for the sake of giving type signatures.
--
-- Because we can't do functor composition without a lot of noise
-- from newtype wrappers, we use @gr@ and @kr@ to precompose the
-- direct/list functor with the reader functor and the return type.
class ZipWithN a gr kr | kr -> gr a where
_zipWithN :: [a -> gr] -> [a] -> kr
instance ZipWithN a b [b] where
_zipWithN = zipWith ($)
instance ZipWithN b gr kr => ZipWithN a (b -> gr) ([b] -> kr) where
_zipWithN = (_zipWithN .) . zipWith ($)
-- | Polyadic version of 'map'/'zipWith'. The given type signature
-- isn't terribly helpful or intuitive. The /real/ type signature
-- is:
--
-- > zipWithN :: {forall a}^N. ({a->}^N r) -> ({[a]->}^N r)
--
-- Note that the @a@ type variables are meta and so are independent
-- from one another, despite being correlated in N across all
-- repetitions.
zipWithN :: (ZipWithN a gr kr) => (a -> gr) -> [a] -> kr
zipWithN = _zipWithN . repeat
If you are just starting to learn Haskell, postpone understanding it for some time :)
A generalization of zip can be achieved using Applicative Notation. It's a bit unpleasant to use because of the newtype wrapping/unwrapping, but if you are doing something that can't be done with a zipWithn
for reasonably small n, you are probably already at a high enough level of abstraction where the notational pains are absent anyway.
The type is ZipList a
, and its applicative instance zips together lists. For example:
(+) <$> ZipList [1,2] <*> ZipList [3,4] == ZipList [4,6]
This generalizes to functions of arbitrary arity and type using partial application:
(+) <$> ZipList [1,2] :: ZipList (Int -> Int)
See how (+) is partially applied here?
If you don't like adding ZipList and getZipList everywhere, you could recreate the notation easily enough:
(<$>) :: (a -> b) -> [a] -> [b]
(<$>) = map
(<*>) :: [a -> b] -> [a] -> [b]
(<*>) = zipWith id
Then the notation for zipWith f a b c d ...
is:
f <$> a <*> b <*> c <*> d <*> ...
Applicative notation is a very powerful and general technique that has much wider scope than just generalized zipping. See the Typeclassopedia for more on Applicative notation.
If all your data is of the same type you could do:
import Data.List (transpose)
zipAllWith :: ([a] -> b) -> [[a]] -> [b]
zipAllWith _ [] = []
zipAllWith f xss = map f . transpose $ xss
zipAll = zipAllWith id
Example:
> zipAll [[1, 2, 3], [4, 5, 6], [7, 8]]
[[1,4,7],[2,5,8],[3,6]]
For a specific number of lists, you can so something like this:
> let l1 = [1,2,3]
> let l2 = "abc"
> let l3 = [10.0, 11.0, 12.0]
> let l4 = [True, False, False]
> [ (e1,e2,e3,e4) | (((e1,e2),e3),e4) <- zip (zip (zip l1 l2) l3) l4 ]
[(1,'a',10.0,True),(2,'b',11.0,False),(3,'c',12.0,False)]
It's not a generic function, but a pattern you can apply to a different number of lists.