2010-11-16 16 views
20

W Pythonie mogę to zrobić:Wyliczyć kolejność w Clojure?

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

które wyjścia:

0 dog 
1 cat 
2 bird 

Jak chciałbym osiągnąć to samo w Clojure? Uważałem podstawie listy ze zrozumieniem tak:

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

Ale to wypisuje każdą kombinację liczby i zwierzęcia. Zgaduję, że jest prosty i elegancki sposób, ale nie widzę tego.

Odpowiedz

33

Jest map-indexed w rdzeniu, co stanowi 1,2.

Twój przykład byłoby:

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

Szybkie rozwiązanie:

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

Albo, jeśli chcesz zawinąć go w funkcję:

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

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

Co się dzieje tutaj jest wektorem funkcja jest stosowana do każdego elementu w obu sekwencjach, a wynik jest zbierany w leniwym zbiorze.

Dalej, spróbuj w repl.

9

Zastosowanie indeksowane od clojure.contrib.seq:

Zastosowanie: (indexed s) Zwraca leniwy sekwencja [indeks, poz] parach, gdzie przedmioty pochodzą z „s” i indeksy liczą się od zera.

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

Dla przykładu to

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

heh. Spójrz na kod źródłowy dla funkcji indeksowanych: https://github.com/clojure/clojure-contrib/blob/b8d2743d3a89e13fc9deb2844ca2167b34aaa9b6/src/main/clojure/clojure/contrib/seq.clj#L51 – Leonel

+0

heh. Wiem. Zastanawiam się, dlaczego w swoim przykładzie nazwałaś funkcję "enum", a następnie :-) – ordnungswidrig

4

map-indexed wygląda w porządku, ale: Czy naprawdę potrzebujemy wszystkich rzeczy doseq i dekonstrukcji args w innych odpowiedzi?

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

EDIT: jak zauważył @gits to działa w REPL ale nie szanuję clojure jest leniwy domyślnie. dorun wydaje się być najbliższym among doseq, doall and doseq dla tego. doseq, jakkolwiek, wydaje się być idiomatycznym faworytem tutaj.

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

'map-indexed' zwraca leniwy seq. 'doseq' jest konieczne, aby zapewnić efekty uboczne, które nastąpią teraz, nie później (w tym przypadku druk wykonywany przez' println'). – glts

+0

@Git prawo i dzięki, Zauważyłem, ale nie zaktualizowałem, ponieważ wciąż się uczę i nie wiem, czy doseq jest tutaj prawdziwym ulubionym idiomem. –

1

Jeszcze innym rozwiązaniem jest stosowanie reduce-kv, które pary elementów, z zastosowaniem wektora z ich pozycjach.

Zatem

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

lub być może nieco bardziej wyraźne

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

Nie chciałbym wybrać to rozwiązanie w stosunku do jednego z doseq tutaj, ale dobrze jest mieć świadomość tej specjalizacji dla wektorów w reduce-kv.