在 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 实现这种非平凡的逻辑更难(编写和读取)并且需要更多代码(在looped 版本中,为价格添加一个更多参数就足够了循环args)。所以我同意@ammaloy 的观点,对于更简单的情况reduce 更好,但更复杂的事情需要更多的低级构造,例如loop/recur 对。