Clojure:顺序回到矢量

时间:2010-01-01 18:50:41

标签: clojure

如何在序列生成操作(如排序)后将序列强制转换为向量?在矢量序列上使用(vec ..)是否代价高昂?

一种(不好?)可能性是不按顺序创建一个新的向量:

(vec (sort [1 2 3 4 5 6]))

我问,因为我需要随机访问(第n ..)到巨大的排序向量 - 现在是排序后的巨大序列,可怕的O(n)随机访问时间

4 个答案:

答案 0 :(得分:7)

Meikel Brandmeyer刚刚在Clojure小组上发布了一个解决方案。

(defn sorted-vec
  [coll]
  (let [arr (into-array coll)]
    (java.util.Arrays/sort arr)
    (vec arr)))

Clojure的sort返回一个排序数组的seq;这种方法做了很多相同的事情,但返回一个向量,而不是seq。

如果您愿意,您甚至可以跳过转换回Clojure持久数据结构:

(defn sorted-arr
  "Returns a *mutable* array!"
  [coll]
  (doto (into-array coll)]
    (java.util.Arrays/sort))

但是生成的Java数组(在大多数情况下可以将其视为Clojure集合)将是可变的。如果您没有将其交给其他代码,那就没问题,但要小心。

答案 1 :(得分:5)

从我自己的测试(没有任何科学性)中,如果你进行大量的排序,你可能会更好地直接在数组上工作。但是如果您很少排序并且有很多随机访问权限,那么使用向量可能是更好的选择,因为随机访问时间平均快40%以上,但由于将向量转换为向量,排序性能非常糟糕一个数组,然后回到一个向量。这是我的发现:

(def foo (int-array (range 1000)))

(time
  (dotimes [_ 10000]
    (java.util.Arrays/sort foo)))

; Elapsed time: 652.185436 msecs

(time
  (dotimes [_ 10000]
    (nth foo (rand-int 1000))))

; Elapsed time: 7.900073 msecs

(def bar (vec (range 1000)))

(time
  (dotimes [_ 10000]
    (vec (sort bar))))

; Elapsed time: 2810.877103 msecs

(time
  (dotimes [_ 10000]
    (nth bar (rand-int 1000))))

; Elapsed time: 5.500802 msecs

P.S。:请注意,矢量版本实际上并没有将排序后的矢量存储在任何地方,但是这不应该大大改变结果,因为你会在循环中使用简单的绑定来提高速度。

答案 2 :(得分:4)

如果你需要随机访问带有大向量的排序结果,那么调用vec所花费的时间应远远超过这样做的时间节省。

如果你发现它并且发现它太慢了,你可能不得不使用java数组。

答案 3 :(得分:-1)

作为一名新的Clojure开发人员,很容易混淆集合和序列。

这个有序矢量函数:

(排序[1 2 3 4 5 6]) => (1 2 3 4 5 6);返回一个序列

但我需要一个矢量用于下一个操作,因为这不起作用......

(需要时间(部分> 3)(1 2 3 4 5 6))

=> ClassCastException java.lang.Long无法强制转换为clojure.lang.IFn user / eval2251(NO_SOURCE_FILE:2136)

让我们尝试将序列转换为向量:

(vec(1 2 3 4 5 6))

=> ClassCastException java.lang.Long无法强制转换为clojure.lang.IFn user / eval2253(NO_SOURCE_FILE:2139)

都能跟得上!但如果你把它们放在一起,它就可以正常工作。

(需要时间(部分> 3)(排序[1 2 3 4 5 6]))

=>(1 2)

课程:你不能直接使用序列!它们是该过程的中间步骤。 当REPL尝试评估(1 2 3 4 5 6)时,它会看到一个函数并抛出异常:

(1 2 3 4 5 6) => ClassCastException java.lang.Long无法强制转换为clojure.lang.IFn user / eval2263(NO_SOURCE_FILE:2146)