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))])
ここでは、最初にベクトルの1番目と2番目の要素のリストを取得し(最初と2番目のベクトルをマップ)、次にそれぞれを個別に操作するため、収集を2回繰り返すことに注意してください。パフォーマンスが重要な場合は、Javaの場合と同じように、反復(loop
フォームまたは末尾再帰を参照)とセットで同じことを行うことができます。パフォーマンスをさらに向上させるために、を使用することもできます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バージョンでは、ループ引数に価格のパラメーターをもう1つ追加するだけで十分です)。したがって、@ ammaloyに同意します。単純な場合の方が優れていますが、より複雑なものには、ペアreduce
などのより低レベルの構成が必要です。loop/recur