在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