2010-11-16 25 views
20

En Python, je peux le faire:Enumerate sur une séquence de Clojure?

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

qui délivre en sortie:

0 dog 
1 cat 
2 bird 

Comment pourrais-je faire la même chose en Clojure? J'ai considéré en utilisant une compréhension de liste comme ceci:

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

Mais ceci imprime chaque combinaison de nombre et d'animal. Je devine qu'il y a une manière simple et élégante de faire ceci mais je ne le vois pas.

Répondre

33

Il est map-indexed dans le noyau à partir de 1.2.

Votre exemple serait:

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

Solution rapide:

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

Ou, si vous voulez l'envelopper dans une fonction:

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

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

Ce qui se passe ici est le vecteur de fonction est appliquée à chaque élément dans les deux séquences, et le résultat est recueilli dans une collection paresseuse.

Allez-y, essayez-le dans votre repl.

9

utilisation indexé à partir de clojure.contrib.seq:

Utilisation: (indexed s) Renvoie une séquence de paresseux [index], article paires, où les éléments viennent de « s » et les indices comptent à partir de zéro.

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

Pour votre exemple c'est

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

heh. Jetez un oeil au code source de la fonction indexée: https://github.com/clojure/clojure-contrib/blob/b8d2743d3a89e13fc9deb2844ca2167b34aaa9b6/src/main/clojure/clojure/contrib/seq.clj#L51 – Leonel

+0

heh. Je connais. Je me demande pourquoi vous avez nommé la fonction 'enum' dans votre exemple, alors :-) – ordnungswidrig

4

map-indexed semble bon, mais: Avons-nous vraiment besoin de tous les trucs doseq et déconstruisant args dans les autres réponses?

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

EDIT: comme l'a noté @gits cela fonctionne dans le REPL, mais ne respecte pas que Clojure est paresseux par défaut. dorun semble être le plus proche among doseq, doall and doseq pour cela. doseq, cependant, semble être le favori idiomatique ici.

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

'map-indexed' renvoie un seq paresseux. 'doseq' est nécessaire pour s'assurer que les effets secondaires se produisent maintenant, pas plus tard (dans ce cas, l'impression effectuée par' println'). – glts

+0

@git droite et merci, j'ai remarqué cela, mais n'a pas mis à jour parce que je suis encore en train d'apprendre et je ne sais pas si doseq est le vrai idiome préféré ici. –

1

Une autre option consiste à utiliser reduce-kv, quelles paires les éléments d'un vecteur avec leurs index.

Ainsi,

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

ou peut-être un peu plus explicite

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

Je ne prendrais pas cette solution, celle avec doseq ici, mais il est bon d'être au courant de cette spécialisation pour les vecteurs dans reduce-kv.