【发布时间】:2010-01-01 18:50:40
【问题描述】:
如何在序列生成操作(如排序)之后将序列转换回向量?在作为向量的序列上使用 (vec..) 成本高吗?
一个(不好的?)可能性是创建一个不按顺序的新向量:
(vec (sort [1 2 3 4 5 6]))
我之所以这么问,是因为我需要随机访问(第 n 个 ..)对巨大的排序向量 - 排序后现在是巨大的序列,具有可怕的 O(n) 随机访问时间
【问题讨论】:
标签: clojure
如何在序列生成操作(如排序)之后将序列转换回向量?在作为向量的序列上使用 (vec..) 成本高吗?
一个(不好的?)可能性是创建一个不按顺序的新向量:
(vec (sort [1 2 3 4 5 6]))
我之所以这么问,是因为我需要随机访问(第 n 个 ..)对巨大的排序向量 - 排序后现在是巨大的序列,具有可怕的 O(n) 随机访问时间
【问题讨论】:
标签: clojure
Meikel Brandmeyer 刚刚在 Clojure 小组上发布了一个解决方案。
(defn sorted-vec
[coll]
(let [arr (into-array coll)]
(java.util.Arrays/sort arr)
(vec arr)))
Clojure 的 sort 返回一个跨排序数组的序列;这种方法做的事情大致相同,但返回的是一个向量,而不是一个序列。
如果您愿意,您甚至可以跳过转换回 Clojure 持久数据结构:
(defn sorted-arr
"Returns a *mutable* array!"
[coll]
(doto (into-array coll)]
(java.util.Arrays/sort))
但生成的 Java 数组(在大多数情况下您可以将其视为 Clojure 集合)将是可变的。如果你不把它交给其他代码也没关系,但要小心。
【讨论】:
根据我自己的测试(不是科学的),在您进行大量排序的情况下,直接处理数组可能会更好。但是,如果您很少排序并且有很多随机访问要做,那么使用向量可能是更好的选择,因为随机访问时间平均快 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.:请注意,矢量版本实际上并未将排序后的矢量存储在任何地方,但这不会显着改变结果,因为您会在循环中使用简单的绑定来提高速度。
【讨论】:
如果您需要对具有大量向量的排序结果进行随机访问,那么调用 vec 所花费的时间应该远远超过这样做所节省的时间。
如果您分析并发现它太慢,您可能不得不使用 java 数组。
【讨论】:
作为一个新的 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 用户/eval2251 (NO_SOURCE_FILE:2136)
让我们尝试将序列转换为向量:
(vec (1 2 3 4 5 6))
=>ClassCastException java.lang.Long 无法转换为 clojure.lang.IFn 用户/eval2253 (NO_SOURCE_FILE:2139)
不!但是如果你把它们放在一起,它就很好用。
(take-while (partial > 3) (sort [1 2 3 4 5 6]))
=>(1 2)
教训:您不能直接使用序列!它们是该过程的中间步骤。 当 REPL 尝试评估 (1 2 3 4 5 6) 时,它会看到一个 a 函数并抛出异常:
(1 2 3 4 5 6) =>ClassCastException java.lang.Long 无法转换为 clojure.lang.IFn 用户/eval2263 (NO_SOURCE_FILE:2146)
【讨论】:
(sort [1 2 3 4 5 6]) 后跟 (take-while (partial > 3) *1) 就可以了。如果只取序列的字符串表示,则会丢失类型信息。