Existe-t-il un équivalent pour la fonction Zip dans Clojure Core ou Contrib?

130

Dans Clojure, je veux combiner deux listes pour donner une liste de paires,

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

Dans Haskell ou Ruby, la fonction est appelée zip . L'implémenter n'est pas difficile, mais je voulais m'assurer de ne pas manquer une fonction dans Core ou Contrib.

Il existe un espace de noms zip dans Core, mais il est décrit comme donnant accès à la technique fonctionnelle Zipper, ce qui ne semble pas être ce que je recherche.

Existe-t-il une fonction équivalente pour combiner 2 ou plusieurs listes, de cette manière, dans Core?

S'il n'y en a pas, est-ce parce qu'il y a une approche idiomatique qui rend la fonction inutile?

John Kane
la source
Il y a une zipfonction dans la bibliothèque Tupelo: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zip
Alan Thompson

Réponses:

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

fait ce que vous voulez:

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

Haskell a besoin d'une collection de fonctions zipWith( zipWith3,, zipWith4...), car elles doivent toutes être d'un type spécifique ; en particulier, le nombre de listes d'entrée qu'ils acceptent doit être fixé. (Le zip, zip2, zip3, ... la famille peut être considérée comme une spécialisation de la zipWithfamille pour le cas d'utilisation commune de Tupling).

En revanche, Clojure et d'autres Lisps supportent bien les fonctions d'arité variable; mapest l'un d'entre eux et peut être utilisé pour "tupling" d'une manière similaire à celle de Haskell

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

La manière idiomatique de construire un "tuple" dans Clojure est de construire un vecteur court, comme illustré ci-dessus.

(Juste pour être complet, notez que Haskell avec certaines extensions de base autorise les fonctions d'arité variable; leur utilisation nécessite une bonne compréhension du langage, cependant, et le vanilla Haskell 98 ne les prend probablement pas en charge du tout, donc les fonctions d'arité fixes sont préférables pour la bibliothèque standard.)

Michał Marczyk
la source
8
Notez que cela se comporte différemment que ziplorsque les collections ne sont pas de la même longueur. Ruby continuera le traitement et fournira des nils pour la collection plus courte, tandis que Clojure arrêtera le traitement une fois que l'une des collections sera épuisée.
Nate W.
@NateW. C'est bon à noter, merci. In Haskell zipse comporte comme Clojure mapà cet égard.
Michał Marczyk
1
Puis-je demander une référence sur les fonctions Haskell à arité variable?
Teodor
22
(partition 2 (interleave '(1 2 3) '(4 5 6))) 
=> ((1 4) (2 5) (3 6))

ou plus généralement

(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
la source
12
(map vector [1 2 3] [4 5 6])
Danlei
la source
11

pour vous donner exactement ce que vous vouliez, le mappage listentre les deux listes vous donnera une liste de listes comme dans votre exemple. Je pense que de nombreux Clojuriens auraient tendance à utiliser des vecteurs pour cela même si cela fonctionnera avec n'importe quoi. et les entrées n'ont pas besoin d'être du même type. map crée des seqs à partir d'eux, puis mappe les seqs afin que toute entrée séq'able fonctionne correctement.

(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
la source
1
Je pense que vous voulez dire hash-map et hash-set au lieu de map and set.
cgrand
3

La manière intégrée serait simplement la fonction 'entrelacer':

(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
lsh
la source
6
pour atteindre l'objectif de l'OP, vous devez ajouter(partition 2 (interleave [1 2 3 4][5 6 7 8]))
skuro
oui - Il semble que je n'ai pas regardé de trop près la sortie souhaitée de l'OP.
LSH
-1

Il existe une fonction appelée zipmap, qui peut avoir un effet similaire, (zipmap (1 2 3)(4 5 6)) La sortie est comme des fllows: {3 6, 2 5, 1 4}

温 发 琥
la source
zipmap vous renvoie une carte, qui ne garantit pas la commande
Ilya Shinkarenko
-2

# (apply map list%) transpose une matrice comme la fonction zip * de Python. En tant que définition de macro:

user => (defmacro py-zip [lst] `(appliquer la liste des cartes ~ lst))

# 'utilisateur / py-zip

utilisateur => (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))

utilisateur => (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
la source
En quoi est-ce mieux que d'utiliser simplement la «carte»? Et à quoi sert une macro ici?
Dave Newton