2010-11-16 14 views
20

In Python posso fare questo:Enumerare su una sequenza in Clojure?

animals = ['dog', 'cat', 'bird'] 
for i, animal in enumerate(animals): 
    print i, animal 

quali uscite:

0 dog 
1 cat 
2 bird 

Come dovrei ottenere la stessa cosa in Clojure? Ho considerato utilizzando una lista di comprensione come questo:

(println 
    (let [animals ["dog" "cat" "bird"]] 
    (for [i (range (count animals)) 
      animal animals] 
     (format "%d %d\n" i animal)))) 

Ma questa stampe fuori ogni combinazione di numero e animali. Immagino che ci sia un modo semplice ed elegante per farlo, ma non lo vedo.

risposta

33

C'è map-indexed nel core di 1.2.

tuo esempio potrebbe essere:

(doseq [[i animal] (map-indexed vector ["dog" "cat" "bird"])] 
    (println i animal)) 
8

Soluzione rapida:

(let [animals ["dog", "cat", "bird"]] 
    (map vector (range) animals)) 

Oppure, se si vuole avvolgerlo in una funzione:

(defn enum [s] 
    (map vector (range) s)) 

(doseq [[i animal] (enum ["dog", "cat", "bird"])] 
    (println i animal)) 

Quello che succede qui è la funzione vettoriale viene applicata a ogni elemento in entrambe le sequenze, e il risultato è raccolto in una collezione pigra.

Vai avanti, provalo nel tuo repl.

9

Uso indicizzato da clojure.contrib.seq:

utilizzati: (indexed s) restituisce una sequenza artificiale di [indice, articolo] coppie, in cui gli articoli vengono da 's' e contare fino indici da zero.

(indexed '(a b c d)) => ([0 a] [1 b] [2 c] [3 d]

Per esempio, questo è

(require 'clojure.contrib.seq) 
(doseq [[i animal] (clojure.contrib.seq/indexed ["dog", "cat", "bird"])] 
    (println i animal)) 
+0

heh. Dai un'occhiata al codice sorgente per la funzione indicizzata: https://github.com/clojure/clojure-contrib/blob/b8d2743d3a89e13fc9deb2844ca2167b34aaa9b6/src/main/clojure/clojure/contrib/seq.clj#L51 – Leonel

+0

heh. Lo so. Mi chiedo perché hai nominato la funzione 'enum' nel tuo esempio, quindi :-) – ordnungswidrig

4

map-indexed guarda a destra, ma: abbiamo davvero bisogno di tutta la roba doseq e decostruendo args nelle altre risposte?

(map-indexed println ["dog", "cat", "bird"]) 

EDIT: come notato da @gits questo funziona nel REPL, ma non rispetto che clojure è pigro per impostazione predefinita. dorun sembra essere il più vicino among doseq, doall and doseq per questo. doseq, comunque, sembra essere il preferito idiomatico qui.

(dorun (map-indexed println ["dog", "cat", "bird"])) 
+1

'map-indexed' restituisce un pigro seq. 'doseq' è necessario per garantire che gli effetti collaterali avvengano ora, non più tardi (in questo caso, la stampa viene effettuata da' println'). – glts

+0

@git giusto e grazie, l'ho notato ma non ho aggiornato perché sto ancora imparando e non so se doseq è il vero idioma preferito qui. –

1

Un'altra opzione è quella di utilizzare reduce-kv, quali coppie gli elementi di un vettore con i loro indici.

Così,

(reduce-kv #(println %2 %3) nil ["dog" "cat" "bird"]) 

o forse leggermente più esplicito

(reduce-kv (fn [_ i animal] (println i animal)) nil ["dog" "cat" "bird"]) 

non vorrei scegliere questa soluzione sopra quello con doseq qui, ma è bene essere consapevoli di questa specializzazione per i vettori in reduce-kv.

Problemi correlati