(doseq [e coll1]
(myfunc e))
非常快,如果你关心的都是副作用。如果我希望myfunc
“并行”从多个集合中获取元素,即将myfunc
应用于每个集合的第一个元素,然后应用于所有第二个元素,然后应用于所有第三个元素,该怎么办?等等?请注意,这是关于for
与doseq
的功能的问题,但如果想要将序列作为输出,map
将执行所需的操作,因此for
不是没必要。
(doseq [e1 coll1
e2 coll2]
(myfunc e1 e2))
会将myfunc
应用于两个集合中所有可能的元素组合。如果我事先知道集合的元素是什么,我可以使用:when
测试来仅合并某些元素,但假设我不知道它?
一种解决方案是创建ntuples以避免笛卡尔积,但这很耗时,从而消除了首先使用doseq
的速度优势:
(let [argvecs (map vector coll1 coll2)] ; seq of ntuples of interleaved vals
(doseq [args argvecs]
(apply myfunc args))))
(这可能比单一集合doseq
慢约8倍。查看this question末尾的domap1
和domap17
的时间。)
答案 0 :(得分:4)
如果你想避免使用map创建元组的开销,你所能做的就是自己编写,作为循环/重复,手动遍历每个集合。但实际上,您仍然需要创建一个元组,以便(apply f args)
可以args
,其中apply
是每个集合的第n项。你可以通过不列出这样的元组来保存一些缺点,但这就是全部。像这样的可变函数的大部分费用是调用{{1}},并构建列表来实现这一点。您可以通过写一个2-arity版本的doseq-sibling和3-arity来避免这种情况,并且......但是n-arity版本总是会慢一些。
答案 1 :(得分:3)
如果是你的速度,你应该打开reflection-warnings,然后查看loop-primitive(用(休息coll1)(休息时间)继续...)
快速结帐Clojure is still和效果测试框架Criterium,以确保您正在衡量正确的事情。
答案 2 :(得分:1)
使用(dorun (map f coll1 coll2 ..))
或(dorun (map apply f colls))
。
您对f
提出的问题越多,就会越长。
(def a (atom 0)
(defn f [& args] (swap! a #(apply + % args)))
(def N 10000)
在单个集合中使用doseq
。 lazy-seq结构可以避免开销。
(bench (doseq [e (range N)] (f e)))
Execution time mean : 4.959713 ms
(bench (dorun (map f (range N))))
Execution time mean : 5.669721 ms
在两个集合中,注意f
必须添加两次而不是一次,所以我期望这需要两倍的时间。现在注意两个版本都有一些结构性开销。
(bench (let [argvecs (map vector (range N) (range N))]
(doseq [e argvecs] (apply f e))))
Execution time mean : 11.876843 ms
(bench (dorun (map f (range N) (range N))))
Execution time mean : 11.145435 ms
答案 3 :(得分:0)
如果我理解得很好,这就是map
的作用:
(map + [1 2] [3 4])
; => (4 6)
如果您只想要效果,可以在结果地图中使用dorun
:
(dorun (map (comp println +) [1 2] [3 4]))
; => nil