Esiste un equivalente per la funzione Zip in Clojure Core o Contrib?


130

In Clojure, voglio combinare due elenchi per dare un elenco di coppie,

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

In Haskell o Ruby la funzione è chiamata zip . L'implementazione non è difficile, ma volevo assicurarmi di non perdere una funzione in Core o Contrib.

C'è un namespace zip in Core, ma è descritto come fornire accesso alla tecnica funzionale Zipper, che non sembra essere ciò che sto cercando.

Esiste una funzione equivalente per combinare 2 o più elenchi, in questo modo, in Core?

In caso contrario, è perché esiste un approccio idiomatico che rende la funzione non necessaria?


C'è una zipfunzione nella libreria Tupelo: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zip
Alan Thompson

Risposte:


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

fa quello che vuoi:

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

Haskell ha bisogno di una raccolta di zipWith( zipWith3, zipWith4, ...) funzioni, perché tutti hanno bisogno di essere di un determinato tipo ; in particolare, è necessario fissare il numero di elenchi di input che accettano. (Il zip, zip2, zip3, ... la famiglia può essere considerata come una specializzazione della zipWithfamiglia per il caso d'uso comune tupling).

Al contrario, Clojure e altri Lisps hanno un buon supporto per le funzioni di arity variabile; mapè uno di questi e può essere usato per "rannicchiare" in un modo simile a quello di Haskell

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

Il modo idiomatico di costruire una "tupla" in Clojure è costruire un vettore breve, come mostrato sopra.

(Solo per completezza, si noti che Haskell con alcune estensioni di base consente funzioni di arity variabili; il loro utilizzo richiede una buona comprensione della lingua, tuttavia, e la vaniglia Haskell 98 probabilmente non le supporta affatto, quindi sono preferibili funzioni di arity fisse per la libreria standard.)


8
Si noti che ciò si comporta in modo diverso rispetto a zipquando le raccolte non hanno la stessa lunghezza. Ruby continuerà l'elaborazione e fornirà nili messaggi per la raccolta più breve, mentre Clojure interromperà l'elaborazione una volta esaurita una delle raccolte.
Nate W.

@NateW. È bello notare, grazie. In Haskell zipsi comporta come Clojure mapin questo senso.
Michał Marczyk,

1
Posso chiedere un riferimento sulle funzioni di Haskell a arità variabile?
Teodor,

22
(partition 2 (interleave '(1 2 3) '(4 5 6))) 
=> ((1 4) (2 5) (3 6))

o più in generale

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


11

per darti esattamente quello che volevi, la mappatura listtra i due elenchi ti darà un elenco di elenchi come nel tuo esempio. Penso che molti Clojuriani tendano a usare i vettori per questo, sebbene funzionino con qualsiasi cosa. e gli input non devono essere dello stesso tipo. map crea seq da loro e quindi mappa i seq in modo che qualsiasi input seq'able funzioni bene.

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

1
Penso che intendi hash-map e hash-set invece di map e set.
cgrand

3

Il modo integrato sarebbe semplicemente la funzione 'interleave':

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

6
per raggiungere l'obiettivo del PO dovresti aggiungere(partition 2 (interleave [1 2 3 4][5 6 7 8]))
skuro

sì - Sembra che non ho esaminato troppo da vicino l'output desiderato dell'OP.
lsh

-1

Esiste una funzione chiamata zipmap, che può avere l'effetto simile, (zipmap (1 2 3)(4 5 6)) L'output è come fllows: {3 6, 2 5, 1 4}


zipmap ti restituisce una mappa, che non garantisce l'ordine
Ilya Shinkarenko

-2

# (applica l'elenco di mappe%) traspone una matrice proprio come la funzione zip * di Python. Come definizione macro:

user => (defmacro py-zip [lst] `(applica l'elenco mappe ~ lst))

# 'User / py-zip

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

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


In che modo è meglio che usare semplicemente 'map'? E qual è il punto di una macro qui?
Dave Newton,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.