如何在序列生成操作(如排序)后将序列强制转换为向量?在矢量序列上使用(vec ..)是否代价高昂?
一种(不好?)可能性是不按顺序创建一个新的向量:
(vec (sort [1 2 3 4 5 6]))
我问,因为我需要随机访问(第n ..)到巨大的排序向量 - 现在是排序后的巨大序列,可怕的O(n)随机访问时间
答案 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)