¿Existe un equivalente para la función Zip en Clojure Core o Contrib?

130

En Clojure, quiero combinar dos listas para dar una lista de pares,

> (zip '(1 2 3) '(4 5 6))  
((1 4) (2 5) (3 6))

En Haskell o Ruby, la función se llama zip . Implementarlo no es difícil, pero quería asegurarme de que no me faltaba una función en Core o Contrib.

Hay un espacio de nombres zip en Core, pero se describe como proporcionar acceso a la técnica funcional Zipper, que no parece ser lo que busco.

¿Existe una función equivalente para combinar 2 o más listas, de esta manera, en Core?

Si no lo hay, ¿es porque hay un enfoque idiomático que hace que la función sea innecesaria?

John Kane
fuente
Hay una zipfunción en la biblioteca de Tupelo: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zip
Alan Thompson

Respuestas:

220
(map vector '(1 2 3) '(4 5 6))

hace lo que quieres:

=> ([1 4] [2 5] [3 6])

Haskell necesita una colección de zipWith( zipWith3, zipWith4, ...) funciones, ya que todos tenemos que ser de un determinado tipo ; en particular, el número de listas de entrada que aceptan debe corregirse. (El zip, zip2, zip3, ... familia puede ser considerada como una especialización de la zipWithfamilia para el caso de uso común de tupling).

En contraste, Clojure y otros Lisps tienen buen soporte para funciones de aridad variable; mapes uno de ellos y se puede usar para "tupling" de una manera similar a la de Haskell

zipWith (\x y -> (x, y))

La forma idiomática de construir una "tupla" en Clojure es construir un vector corto, como se muestra arriba.

(Solo para completar, tenga en cuenta que Haskell con algunas extensiones básicas sí permite funciones de arity variables; sin embargo, su uso requiere una buena comprensión del lenguaje, y el Haskell 98 de vainilla probablemente no las admita en absoluto, por lo tanto, las funciones de arity fijas son preferibles para la biblioteca estándar).

Michał Marczyk
fuente
8
Tenga en cuenta que esto se comporta de manera diferente que zipcuando las colecciones no tienen la misma longitud. Ruby continuará procesando y suministrando nils para la colección más corta, mientras que Clojure dejará de procesar una vez que se agote una de las colecciones.
Nate W.
@NateW. Es bueno tener en cuenta, gracias. En Haskell se zipcomporta como Clojure mapa este respecto.
Michał Marczyk
1
¿Puedo pedir una referencia sobre las funciones de Haskell de arity variable?
Teodor
22
(partition 2 (interleave '(1 2 3) '(4 5 6))) 
=> ((1 4) (2 5) (3 6))

o más generalmente

(defn zip [& colls]
  (partition (count colls) (apply interleave colls)))

(zip '( 1 2 3) '(4 5 6))           ;=> ((1 4) (2 5) (3 6))

(zip '( 1 2 3) '(4 5 6) '(2 4 8))  ;=> ((1 4 2) (2 5 4) (3 6 8))
Lambder
fuente
12
(map vector [1 2 3] [4 5 6])
danlei
fuente
11

para darle exactamente lo que quería, el mapeo listen las dos listas le dará una lista de listas como en su ejemplo. Creo que muchos Clojurianos tenderían a usar vectores para esto, aunque funcionaría con cualquier cosa. y las entradas no necesitan ser del mismo tipo. map crea seqs a partir de ellos y luego asigna las seqs para que cualquier entrada seq'able funcione bien.

(map list '(1 2 3) '(4 5 6))
(map list  [1 2 3] '(4 5 6))
(map hash-map  '(1 2 3) '(4 5 6))
(map hash-set  '(1 2 3) '(4 5 6))
Arthur Ulfeldt
fuente
1
Creo que te refieres a hash-map y hash-set en lugar de map and set.
cgrand
3

La forma integrada sería simplemente la función 'intercalar':

(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
lsh
fuente
66
para lograr el objetivo del OP que debe agregar(partition 2 (interleave [1 2 3 4][5 6 7 8]))
skuro
sí, parece que no miré la salida deseada del OP demasiado de cerca.
lsh
-1

Hay una función llamada zipmap, que puede tener un efecto similar, (zipmap (1 2 3)(4 5 6)) La salida es como fllows: {3 6, 2 5, 1 4}

温 发 琥
fuente
zipmap le devuelve un mapa, que no garantiza el pedido
Ilya Shinkarenko
-2

# (aplicar lista de mapas%) transpone una matriz al igual que la función Python zip *. Como una definición macro:

usuario => (defmacro py-zip [lst] `(aplicar lista de mapas ~ lst))

# 'usuario / py-zip

usuario => (py-zip '((1 2 3 4) (9 9 9 9) (5 6 7 8)))

((1 9 5) (2 9 6) (3 9 7) (4 9 8))

usuario => (py-zip '((1 9 5) (2 9 6) (3 9 7) (4 9 8)))

((1 2 3 4) (9 9 9 9) (5 6 7 8))

dhr bibberknie
fuente
¿Cómo es esto mejor que simplemente usar 'mapa'? ¿Y cuál es el punto de una macro aquí?
Dave Newton