2010-03-18 18 views
16

En la función python zip acepta un número arbitrario de listas y las comprime juntas.Cómo comprimir varias listas en Haskell?

>>> 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)] 
>>> 

¿Cómo puedo zip juntos varias listas en Haskell?

+1

Esta es la función zip3. –

+1

Sí, 'zip3' es para comprimir 3 listas. –

Respuesta

33

Una generalización de la postal se puede lograr utilizando Applicative Notation. Es un poco desagradable de usar debido a la nueva envoltura/desempaquetado, pero si estás haciendo algo que no se puede hacer con un zipWithn por un n razonablemente pequeño, probablemente ya estés en un nivel de abstracción lo suficientemente alto donde los dolores notacionales son ausente de todos modos.

El tipo es ZipList a, y su instancia aplica comprime juntas listas. Por ejemplo:

(+) <$> ZipList [1,2] <*> ZipList [3,4] == ZipList [4,6] 

Este generaliza a funciones de aridad arbitraria y escribir mediante la aplicación parcial:

(+) <$> ZipList [1,2] :: ZipList (Int -> Int) 

Ver cómo (+) se aplica parcialmente aquí?

Si no te gusta la adición de ZipList y getZipList todas partes, se puede recrear la notación con bastante facilidad:

(<$>) :: (a -> b) -> [a] -> [b] 
(<$>) = map 

(<*>) :: [a -> b] -> [a] -> [b] 
(<*>) = zipWith ($) 

A continuación, la notación para zipWith f a b c d ... es:

f <$> a <*> b <*> c <*> d <*> ... 

notación Aplicativo es una muy técnica poderosa y general que tiene un alcance mucho más amplio que el simple zipping generalizado. Consulte el Typeclassopedia para obtener más información sobre la notación Aplicativa.

+3

Para ayudar a abordar el nuevo problema de envoltura/desenvolvimiento de tipo nuevo, consulte el paquete newtype http://hackage.haskell.org/package/newtype –

+0

El enlace Typeclassopedia está dando un 404. – sean

+0

@sean, corregido, gracias – luqui

9

Parece que también hay una zip3 (doc) y una función zip4 (doc) en Haskell. Pero el zipn parece ser complicado debido al sistema de tipo fuerte. Here is a good discussion que encontré durante mi investigación.

+0

Los ejemplos correspondientes para 'zip3': http://www.zvon.org/other/haskell/Outputprelude/zip3_f.html – kennytm

+0

¿Quiere decir que es IMPOSIBLE en haskell? –

+0

No lo sé. Tal vez alguien más sepa un truco. Pero creo que las posibilidades son bajas, porque no hubo una buena respuesta en la lista de correo y la biblioteca estándar ha implementado todas las funciones zip2, zip3, etc. en su lugar ... – tux21b

20

puede transponer una lista de listas:

>>> import Data.List 
>>> transpose [l1,l2,l3] 
[[1,5,7],[2,6,4],[3,7,8]] 
+2

Funciona solo si todos los argumentos tienen el mismo tipo, y esa es también la razón por la que parece ser tan difícil implementar un zipN general. – tux21b

+0

hay una diferencia en el comportamiento: transponer [[1,2,3], [4,5,6,7]] => [[1,4], [2,5], [3,6], [7] ] zip no incluye ese último elemento. Pero aparte de eso, ¡es un ganador! Definitivamente se une a mi caja de herramientas. –

+0

He escrito esta función para evitar el comportamiento diferente en caso de que las longitudes de las listas no coincidan: 'zipLists list = takeWhile (\ x -> length x == length list) $ transpose list' –

3

Es no trivial, pero es factible. Ver this blog post. No sé si esto se convirtió en alguna biblioteca.

Aquí está another version, que es más simple. Este hecho se podría cortar-n-pegar aquí:

{-# 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 @[email protected] 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 @[email protected] and @[email protected] 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 @[email protected] 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 

Si usted está recién empezando a aprender Haskell, posponer la comprensión de que durante algún tiempo :)

+0

No puedo entender una palabra. Solo tengo un conocimiento básico de Haskell. ¿Puedes extraer esa función de esa publicación y publicarla aquí? –

0

Si todos los datos son del mismo tipo que podría hacer:

import Data.List (transpose) 

zipAllWith :: ([a] -> b) -> [[a]] -> [b] 
zipAllWith _ [] = [] 
zipAllWith f xss = map f . transpose $ xss 

zipAll = zipAllWith id 

Ejemplo:

> zipAll [[1, 2, 3], [4, 5, 6], [7, 8]] 
[[1,4,7],[2,5,8],[3,6]] 
1

Para un número específico de listas, puede por lo menos así:

> 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)] 

No es una función genérica, sino un patrón que puede aplicar a un número diferente de listas.

6

GHC also supports parallel list comprehensions:

{-# LANGUAGE ParallelListComp #-} 

[(x,y) | x <- [1..3] 
     | y <- ['a'..'c'] 
     ] 

==> [(1,'a'),(2,'b'),(3,'c')] 

Acabo de prueba que hasta 26 variables paralelas, que debería ser suficiente para todos los fines prácticos.

Sin embargo, es un poco hacky (y no estándar), por lo que si está escribiendo algo serio, ZipList puede ser la mejor manera de hacerlo.

+0

La principal diferencia entre esto y la comprensión de la lista normal es que x ya no está en el alcance en la parte de y de la comprensión correcta? – CMCDragonkai

3

Generalizar la compresión es bastante fácil. Sólo tienes que escribir versiones especializadas de los Applicative combinadores para ZipList:

z :: [a -> b] -> [a] -> [b] 
z = zipWith ($) 

infixl 4 `z` 

Ahora se puede comprimir tantas listas como desee:

f <$> xs `z` ys `z` zs 

o alternativamente:

repeat f `z` xs `z` ys `z` zs 
4

I Creo que es probablemente la solución menos elegante sugerida, pero para completar, debería agregarse que tales cosas deberían ser posibles con Templa te Haskell.

Este fue en realidad están recogidas en lo que creo que es el documento original Plantilla Haskell (búsqueda zipn en el texto): http://research.microsoft.com/en-us/um/people/simonpj/Papers/meta-haskell/meta-haskell.pdf

pero creo que el código de hecho nunca trabajó, ver esto: http://www.haskell.org/pipermail/template-haskell/2003-July/000126.html (rebanadas de patrón no están implementadas).

Eso no fue implementado en 2003, pero todavía no está implementado hoy: http://www.haskell.org/ghc/docs/7.6.1/html/users_guide/template-haskell.html (rodajas de patrones no son compatibles)

Sin embargo, existe una implementación de zipWithN usando Haskell plantilla: http://www.haskell.org/haskellwiki/Template_Haskell#zipWithN

tengo verificó que funciona con este programa de prueba:

{-# LANGUAGE TemplateHaskell #-} 
import Zipn 

main = do 
    let l1 = [1,2,3] 
    let l2 = [5,6,7] 
    let l3 = [7,4,8] 
    print $ $(zipWithN 3) (,,) l1 l2 l3 

En el módulo Zipn, pegué la zipn, simplemente renombrado zipWithN para c laridad (y recuerde agregar el pragma TemplateHaskell en la parte superior). Tenga en cuenta que la N está, de hecho, grabada aquí dos veces, porque tuve que dar (,,) como la función "con". Tendría que cambiar el número de comas según N.

(,,) representa \a b c -> (a,b,c)

supongo que alguien con buena plantilla habilidades Haskell (que no es mi caso en este punto) podría hacer una zipN recta usando la plantilla Haskell.

Cuestiones relacionadas