Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 为什么这个循环函数比map慢?_Clojure_Lazy Sequences - Fatal编程技术网

Clojure 为什么这个循环函数比map慢?

Clojure 为什么这个循环函数比map慢?,clojure,lazy-sequences,Clojure,Lazy Sequences,我看了maps源代码,它基本上一直在创建惰性序列。我认为对集合进行迭代并将其添加到瞬态向量会更快,但显然不是这样。我对clojures的行为有什么不理解的 ;=> (time (do-with / (range 1 1000) (range 1 1000))) ;"Elapsed time: 23.1808 msecs" ; ; vs ;=> (time (doall (map #(/ %1 %2) (range 1 1000) (range 1 1000)))) ;"Elapsed

我看了maps源代码,它基本上一直在创建惰性序列。我认为对集合进行迭代并将其添加到瞬态向量会更快,但显然不是这样。我对clojures的行为有什么不理解的

;=> (time (do-with / (range 1 1000) (range 1 1000)))
;"Elapsed time: 23.1808 msecs"
;
; vs
;=> (time (doall (map #(/ %1 %2) (range 1 1000) (range 1 1000))))
;"Elapsed time: 2.604174 msecs"
(defn do-with
  [fn coll1 coll2]
  (let [end (count coll1)]
    (loop [i   0
           res (transient [])]
        (if
          (= i end)
            (persistent! res)
            (let [x (nth coll1 i)
                  y (nth coll2 i)
                  r (fn x y)]
              (recur (inc i) (conj! res r))) 
                  ))))

根据对相关结果的推测影响顺序:

  • 您的
    do with
    函数使用
    nth
    访问输入集合中的单个项
    n次
    在范围内以线性时间运行,使
    二次方式运行。不用说,这将降低大型集合的性能

  • range
    生成分块的seq,并且
    map
    非常有效地处理这些seq。(本质上,它通过依次在每个输入块的内部数组上运行紧密循环,将结果放置在输出块的内部数组中,从而生成最多32个元素的块(这里实际上正好是32个元素)

  • 使用
    时间进行基准测试
    不会给您带来稳定的性能。(这就是为什么人们应该真正使用适当的基准库;在Clojure的例子中,这是标准解决方案。)

  • 顺便提一下,
    (map#(/%1%2)xs-ys)
    可以简单地写成
    (map/xs-ys)

    更新:

    我使用
    (范围1 1000)
    作为每种情况下的两种输入(如问题文本中),对
    地图
    版本、原始
    do with
    版本和新的
    do with
    版本进行了基准测试,获得了以下平均执行时间:

    ;;; (range 1 1000)
    new do-with           170.383334 µs
    (doall (map ...))     230.756753 µs
    original do-with       15.624444 ms
    
    此外,我使用存储在Var中的向量作为输入而不是范围(即,在开始时使用
    (def r(vec(range 1 1000))
    ,并在每个基准中使用
    r
    作为两个集合参数)重复了基准测试。毫不奇怪,最初的
    do with
    首先出现--
    nth
    在向量上非常快(加上对向量使用
    nth
    ,避免了seq遍历中涉及的所有中间分配)

    以下是具有线性时间复杂度的新
    do with

    (defn do-with [f xs ys]
      (loop [xs  (seq xs)
             ys  (seq ys)
             ret (transient [])]
        (if (and xs ys)
          (recur (next xs)
                 (next ys)
                 (conj! ret (f (first xs) (first ys))))
          (persistent! ret))))
    

    感谢您的详细和全面的答复。看起来我需要更清楚地知道我正在对什么类型的数据结构执行什么类型的操作。
    (defn do-with [f xs ys]
      (loop [xs  (seq xs)
             ys  (seq ys)
             ret (transient [])]
        (if (and xs ys)
          (recur (next xs)
                 (next ys)
                 (conj! ret (f (first xs) (first ys))))
          (persistent! ret))))