Vector Clojure:使用索引为向量中的每个元素调用函数

Vector Clojure:使用索引为向量中的每个元素调用函数,vector,clojure,loops,Vector,Clojure,Loops,假设我有一个向量: (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 [i

假设我有一个向量:

(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呢?

这种方法是惯用的(实际上与
clojure.contrib.seq utils/index
相同)。如果确实希望避免额外的数据结构,可以执行以下操作:

(loop [data data, index 0]
  (when (seq data)
    (setCell 0 index (first data))
    (recur (rest data) (inc index))))

除非有很好的理由不这样做,否则我会使用你的版本。

你可以用一种非常简单的惯用方式获得同样的效果,只需将索引与数据一起映射即可

(map #(setCell 0 %1 %2) (iterate inc 0) data)

您可能希望将其包装在一个
(doall
(doseq
)中,以使调用立即发生。将无限seq与有限seq一起映射是很好的,因为当最短的seq用完时,映射将停止。

我对迄今为止选项的性能做了一个简短的比较:

; 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's suggestion
(def total 0.0)
(time (doall (map #(testThis %1 %2) (iterate inc 0) testD)))
(println "Total: " total)

; time using Brian's 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)
在我的单芯笔记本电脑上的结果:

   "Elapsed time: 598.224635 msecs"
   Total:  9.9999E9
   "Elapsed time: 241.573161 msecs"
   Total:  9.9999E9
   "Elapsed time: 959.050662 msecs"
   Total:  9.9999E9
初步结论:


使用循环/重复解决方案。

最好的方法是使用
clojure.contrib.seq utils/index
,如下所示(使用解构):


游戏进行得有点晚了,但对于访问此页面的用户来说:现在(从clojure 1.2开始)clojure.core中有一个
map index
函数可用


一个问题(除非我弄错了):没有“pmap”等价的,这意味着映射索引计算不容易并行化。在这种情况下,我会参考上面提供的解决方案。

Rich建议微基准测试运行几十次每个测试,然后使用最后一个测试,首先让热点优化器在函数上预热。测试这比映射函数轻得多因此,尽可能紧密的循环会更好。我想我需要使用热点优化进行测试。我刚刚在Python中运行了完全相同的测试,它在80毫秒内运行。编写代码,说明它所做的事情和所做的事情。稍后优化——至少在收集需求并完成分析之后。我添加了(dotimes[n 100]…)围绕我的测试,得到了相同的结果,hotspot似乎无法解决这些差异。Recur版本大约需要230毫秒。索引向量大约690毫秒。糟糕。我们为什么要对此进行基准测试?如果你仔细考虑你的程序何时处于什么状态,那么无限制可变状态比函数式编程快。Doe这是不是意味着我们不应该做函数式编程?当然不是。同样,说“使用循环/重复”是荒谬的,因为它更快;使用
映射索引通常会更清晰或更简单。很好,我不知道映射在应用于多个集合时的这种行为。3年随访:(iterate inc 0)最好使用just(range)编写Clojure 1.2添加了
map indexed
,这是迄今为止我见过的最简单和最好的选项。请参见。至于
indexed
函数去了哪里?文档似乎没有对此做任何说明。我最后只写了一个。
(defn indexed[coll](map indexed vector coll))
,只是因为有时候
(对于[idx val](索引coll)(..)
生成的代码比映射索引的代码更清晰。您可以通过将递增迭代器作为pmap的第一个集合来执行
pmap索引:
(defn pmap indexed[f coll](pmap f(iterate inc 0)coll))
(doseq [[idx val] (indexed ["Hello" "World" "Test" "This"])]
  (setCell 0 idx val))