对于Clojure来说有点新鲜,我似乎无法弄清楚如何做一些看似简单的事情.我只是看不到它.我有一个矢量seq.假设每个向量具有表示客户编号和发票编号的两个值,并且每个向量表示项目的销售.所以它看起来像这样:
([ 100 2000 ] [ 100 2000 ] [ 101 2001 ] [ 100 2002 ])
Run Code Online (Sandbox Code Playgroud)
我想计算独特客户和独特发票的数量.所以这个例子应该产生矢量
[ 2 3 ]
Run Code Online (Sandbox Code Playgroud)
在Java或其他命令式语言中,我将循环遍历seq中的每个向量,将客户编号和发票编号添加到集合中,然后计算每个集合中的值的数量并将其返回.我看不到这样做的功能方法.
谢谢您的帮助.
编辑:我应该在我原来的问题中指出,矢量的seq是数百万的,实际上只有两个值.所以我想只通过seq一次并计算这些独特的计数(以及一些总和)在seq上运行.
ffr*_*end 11
在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))])
Run Code Online (Sandbox Code Playgroud)
请注意,这里首先获得向量的第一个和第二个元素的列表(映射第一个/第二个向量),然后分别对每个元素进行操作,从而迭代收集两次.如果性能确实很重要,那么你可以用迭代(参见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]
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,不需要原子或类似的东西.首先,将状态传递给每个下一次迭代(重复调用).其次,您使用瞬态来使用临时可变集合(有关详细信息,请参阅有关瞬态的更多信息),从而避免每次都创建新对象.
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]))
Run Code Online (Sandbox Code Playgroud)
在这里,我们在矢量中保存中间结果[custs invs total]
,解压缩,处理并每次将它们打包回矢量.正如您所看到的,实现这样的非平凡逻辑reduce
更难(写入和读取)并且需要更多代码(在loop
ed版本中,它足以为循环args添加一个更多的参数).因此,我同意@ammaloy对于更简单的情况reduce
更好,但更复杂的事情需要更多的低级构造,例如loop/recur
pair.
正如消耗序列时经常出现的情况一样,reduce
比loop
这里更好.你可以这样做:
(map count (reduce (partial map conj)
[#{} #{}]
txn))
Run Code Online (Sandbox Code Playgroud)
或者,如果你真的喜欢瞬态:
(map (comp count persistent!)
(reduce (partial map conj!)
(repeatedly 2 #(transient #{}))
txn))
Run Code Online (Sandbox Code Playgroud)
这两种解决方案都只遍历输入一次,并且它们比循环/重复解决方案所需的代码少得多.