tags:

views:

264

answers:

6

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?

+6  A: 

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.

tux21b
The corresponding examples for `zip3`: http://www.zvon.org/other/haskell/Outputprelude/zip3_f.html
KennyTM
Do you mean its just IMPOSSIBLE in haskell??
TheMachineCharmer
I don't know. Maybe someone other knows a trick. But I think the chances are low, because there wasn't a good answer on the mailing list and the standard library has implemented all those zip2, zip3 etc. functions instead...
tux21b
@KennyTM: thank you, I've added those links to the answer
tux21b
Generalized `zipWithN`: http://www.reddit.com/r/haskell/comments/b9qyp/generalized_zipwithn_with_a_pretty_implementation/
jleedev
Data.List goes up to `zip7`. Arguably 7 items in a tuple is already too many for an easily understood, maintainable program.
Dave Hinton
+4  A: 

You can transpose a list of lists:

>>> import Data.List
>>> transpose [l1,l2,l3]
[[1,5,7],[2,6,4],[3,7,8]]
newacct
Works only if all the arguments are having the same type, and that's also the reason why it seems to be so hard to implement a general zipN.
tux21b
+1  A: 

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&gt;
----------------------------------------------------------------
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 :)

ADEpt
I can't understand a word. I have only very basic knowledge of Haskell. Can you please extract that function from that post and post it here?
TheMachineCharmer
+11  A: 

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.

luqui
A: 

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]]
trinithis
A: 

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.

camh