在Clojure中,你可以做到这一点几乎相同的方式 - 第一次调用distinct
获得独特的值,然后用count
计算结果:
(def vectors (list [ 100 2000 ] [ 100 2000 ] [ 101 2001 ] [ 100 2002 ]))
(defn count-unique [coll]
(count (distinct coll)))
(def result [(count-unique (map first vectors)) (count-unique (map second vectors))])
请注意,在这里你第一次得到的第一和第二的元素列表向量(映射第一/第二向量),然后分别进行操作,从而迭代两次收集。如果性能确实很重要,则可以使用迭代(请参阅loop
表单或尾递归)和集合来做同样的事情,就像在Java中所做的一样。要进一步提高性能,您还可以使用transients
。虽然对于像你这样的初学者,我会推荐第一种方式distinct
。
UPD。这里的版本与循环:
(defn count-unique-vec [coll]
(loop [coll coll, e1 (transient #{}), e2 (transient #{})]
(cond (empty? coll) [(count (persistent! e1)) (count (persistent! e2))]
:else (recur (rest coll)
(conj! e1 (first (first coll)))
(conj! e2 (second (first coll)))))))
(count-unique-vec vectors) ==> [2 3]
正如你所看到的,在原子或类似的东西没有必要。首先,您将状态传递给每个下一个迭代(重复调用)。其次,您使用瞬变来使用临时可变的集合(有关详细信息,请阅读有关瞬变的更多信息),从而避免每次都创建新对象。
UPD2。这里的版本reduce
延长的问题(与价格):
(defn count-with-price
"Takes input of form ([customer invoice price] [customer invoice price] ...)
and produces vector of 3 elements, where 1st and 2nd are counts of unique
customers and invoices and 3rd is total sum of all prices"
[coll]
(let [[custs invs total]
(reduce (fn [[custs invs total] [cust inv price]]
[(conj! custs cust) (conj! invs inv) (+ total price)])
[(transient #{}) (transient #{}) 0]
coll)]
[(count (persistent! custs)) (count (persistent! invs)) total]))
在这里,我们持有一个向量[custs invs total]
中间结果,解压缩,处理和每次收拾他们回到一个载体。正如你所看到的,用reduce
实现这样的平凡逻辑比较困难(既可以写入也可以读取),并且需要更多的代码(在loop
ed版本中,足够为价格添加一个参数来循环参数)。所以我同意@ammaloy,对于更简单的情况reduce
更好,但更复杂的事情需要更多的低级构造,例如loop/recur
对。
谢谢。由于收集的交易数量在几十万之内,因此性能确实非常重要。使用循环形式是否需要使用原子或类似的东西来维持循环的每次迭代之间的状态?这是让我想起来的部分。 – 2012-07-11 12:57:06
@DaveKincaid:看到我的更新。但是请注意,所有解决方案的时间复杂度都是相同的,所以它们的运行时间只会因恒定乘数(可能非常小)而不同。 – ffriend 2012-07-11 13:13:16
这很棒!谢谢。发布我的问题并看到您的第一个回复后。我离开并尝试了一下。这是我想出来的。我想知道你能否帮助我理解你的方法和这个方法之间的差异。 (让客户设置(atom#{})invoice-set(atom#{})](doseq [[customer invoice] txn](swap!customer-set conj customer)(swap!invoice-set conj invoice) )[(count(deref customer-set)))(count(deref invoice-set))])' – 2012-07-11 13:17:53