2010-11-16 1 views
20

В Python я могу это сделать:Перечислить последовательность в Clojure?

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

Какие выходы:

0 dog 
1 cat 
2 bird 

Как бы я сделать то же самое в Clojure? Я рассмотрел использование такого списка:

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

Но это печатает каждую комбинацию числа и животных. Я предполагаю, что есть простой и элегантный способ сделать это, но я этого не вижу.

ответ

33

Существует в ядре, 1,2 map-indexed.

Ваш пример будет:

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

Быстрое решение:

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

Или, если вы хотите, чтобы обернуть его в функции:

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

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

Что происходит здесь является вектор-функция применяется к каждому элементу в обеих последовательностях, и результат собирается в ленивой коллекции.

Вперед, попробуйте в своем реплике.

9

Использование индексируется clojure.contrib.seq:

Использование: (indexed s) Возвращает ленивую последовательность [индекс, п] пар, где элементы приходят от «с» и рассчитывать индексы от нуля.

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

Для примера это

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

heh. Взгляните на исходный код для индексируемой функции: https://github.com/clojure/clojure-contrib/blob/b8d2743d3a89e13fc9deb2844ca2167b34aaa9b6/src/main/clojure/clojure/contrib/seq.clj#L51 – Leonel

+0

heh. Я знаю. Интересно, почему вы назвали функцию «enum» в вашем примере, а затем :-) – ordnungswidrig

4

map-indexed выглядит правильно, но: Действительно ли нам нужны все вещи doseq и деконструкции ARGS в других ответах?

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

EDIT: как отметил @gits это работает в REPL, но не уважает, что Clojure ленив по умолчанию. dorun представляется ближайшим к нему among doseq, doall and doseq. doseq, как бы там ни было, кажется, здесь идиоматический фаворит.

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

'map-indexed' возвращает ленивый seq. 'doseq' необходим, чтобы гарантировать, что побочные эффекты произойдут сейчас, а не позже (в этом случае печать осуществляется с помощью' println'). – glts

+0

@ git right and thanks, я заметил это, но не обновлялся, потому что я все еще участвую и не знаю, является ли доза - настоящей любимой идиомой здесь. –

1

Еще один вариант заключается в использовании reduce-kv, какие пары элементов из вектора с их индексами.

Таким образом,

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

или, возможно, немного более явный

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

Я бы не забрать это решение по одной с doseq здесь, но это хорошо, чтобы быть в курсе этой специализации для векторов в reduce-kv.