7

Clojureに少し慣れていないので、簡単なはずのことを行う方法がわからないようです。見えない。私はベクトルのシーケンスを持っています。各ベクトルに顧客番号と請求書番号を表す2つの値があり、各ベクトルがアイテムの販売を表すとします。したがって、次のようになります。

([ 100 2000 ] [ 100 2000 ] [ 101 2001 ] [ 100 2002 ])

ユニークな顧客とユニークな請求書の数を数えたいです。したがって、例ではベクトルを生成する必要があります

[ 2 3 ]

Javaまたは別の命令型言語では、seqの各ベクトルをループし、顧客番号と請求書番号をセットに追加してから、各セットの値の数を数えて返します。これを行うための機能的な方法がわかりません。

助けてくれてありがとう。

編集:私は元の質問で、ベクトルのシーケンスが数千万であり、実際には2つ以上の値を持っていることを指定する必要がありました。したがって、seqを1回だけ実行し、その1回の実行でこれらの一意のカウント(およびいくつかの合計も)を計算したいと思います。

4

5 に答える 5

12

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は難しく(書き込みと読み取りの両方)、さらに多くのコードが必要です(loopedバージョンでは、ループ引数に価格のパラメーターをもう1つ追加するだけで十分です)。したがって、@ ammaloyに同意します。単純な場合の方が優れていますが、より複雑なものには、ペアreduceなどのより低レベルの構成が必要です。loop/recur

于 2012-07-11T12:44:39.590 に答える
10

シーケンスを消費するときによくあることですが、ここreduceよりも優れloopています。あなたはただすることができます:

(map count (reduce (partial map conj) 
                   [#{} #{}]
                   txn))

または、本当にトランジェントに興味がある場合:

(map (comp count persistent!)
     (reduce (partial map conj!) 
             (repeatedly 2 #(transient #{}))
             txn))

これらのソリューションはどちらも入力を1回だけトラバースし、ループ/繰り返しソリューションよりもはるかに少ないコードで済みます。

于 2012-07-11T18:21:11.243 に答える
4

または、セットを使用して重複排除を処理することもできます。これは、セットが特定の値の最大値を1つ持つことができるためです。

(def vectors '([100 2000] [100 2000] [101 2001] [100 2002]))    
[(count (into #{} (map first vectors)))  (count (into #{} (map second vectors)))]
于 2012-07-11T12:57:18.200 に答える
1

マップと高階関数を使用してこれを行うための優れた方法は次のとおりです。

(apply 
  map 
  (comp count set list) 
  [[ 100 2000 ] [ 100 2000 ] [ 101 2001 ] [ 100 2002 ]])

=> (2 3)
于 2012-07-11T22:45:23.027 に答える
0

また、上記の優れたソリューションに対する他のソリューション:

(map (comp count distinct vector) [ 100 2000 ] [ 100 2000 ] [ 101 2001 ] [ 100 2002 ])

スレッドラストマクロで書かれたその他:

(->> '([100 2000] [100 2000] [101 2001] [100 2002]) (apply map vector) (map distinct) (map count))

両方とも(2 3)を返します。

于 2015-09-03T17:14:14.000 に答える