Clojure: вызывать функцию для каждого элемента в векторе с индексом
Скажем, у меня есть вектор:
(def data ["Hello" "World" "Test" "This"])
И я хочу заполнить таблицу где-нибудь, у которой есть api:
(defn setCell
[row col value]
(some code here))
Тогда какой лучший способ получить следующие вызовы:
(setCell 0 0 "Hello")
(setCell 0 1 "World")
(setCell 0 2 "Test")
(setCell 0 3 "This")
Я обнаружил, что будет работать следующее:
(let [idv (map vector (iterate inc 0) data)]
(doseq [[index value] idv] (setCell 0 index value)))
Но есть ли более быстрый способ, который не требует новой временной структуры данных idv?
Ответы
Ответ 1
То, как вы это делаете, является идиоматическим (и фактически идентичным clojure.contrib.seq-utils/indexed
). Если вы действительно хотите избежать дополнительной структуры данных, вы можете сделать это:
(loop [data data, index 0]
(when (seq data)
(setCell 0 index (first data))
(recur (rest data) (inc index))))
Я бы воспользовался вашей версией, если только не было причин, почему бы и нет.
Ответ 2
Вы можете получить тот же эффект в очень clojure -идиоматическом режиме, просто сопоставляя индексы вместе с данными.
(map #(setCell 0 %1 %2) (iterate inc 0) data)
Вы можете обернуть это в (doall
или (doseq
, чтобы сделать вызовы сейчас. Это просто отлично, чтобы отобразить бесконечное seq вместе с конечным, потому что карта остановится, когда закончится кратчайший seq.
Ответ 3
Немного поздно в игре, но для людей, обращающихся к этой странице: теперь (с clojure 1.2) есть функция map-indexed
, доступная в clojure.core.
Одна проблема (если я не ошибаюсь): нет эквивалента "pmap", что означает, что вычисления с индексированием по карте не могут быть легко распараллелены. В этом случае я хотел бы обратиться к решениям, предложенным выше.
Ответ 4
Самый приятный способ - использовать clojure.contrib.seq-utils/indexed
, который будет выглядеть так (используя деструктурирование):
(doseq [[idx val] (indexed ["Hello" "World" "Test" "This"])]
(setCell 0 idx val))
Ответ 5
Я сделал небольшое сравнение производительности опций sofar:
; just some function that sums stuff
(defn testThis
[i value]
(def total (+ total i value)))
; our test dataset. Make it non-lazy with doall
(def testD (doall (range 100000)))
; time using Arthur suggestion
(def total 0.0)
(time (doall (map #(testThis %1 %2) (iterate inc 0) testD)))
(println "Total: " total)
; time using Brian recursive version
(def total 0.0)
(time (loop [d testD i 0]
(when (seq d)
(testThis i (first d))
(recur (rest d) (inc i)))))
(println "Total: " total)
; with the idiomatic indexed version
(def total 0.0)
(time (let [idv (map vector (iterate inc 0) testD)]
(doseq [[i value] idv] (testThis i value))))
(println "Total: " total)
Результаты на моем 1 основном ноутбуке:
"Elapsed time: 598.224635 msecs"
Total: 9.9999E9
"Elapsed time: 241.573161 msecs"
Total: 9.9999E9
"Elapsed time: 959.050662 msecs"
Total: 9.9999E9
Предварительное заключение:
Используйте решение loop/recur.