7
votes

Clojure - Compter des valeurs uniques des vecteurs dans un SEQ

Être un peu nouveau à Clojure, je n'arrive pas à comprendre comment faire quelque chose qui semble être simple. Je ne peux tout simplement pas le voir. J'ai un SEQ de vecteurs. Disons que chaque vecteur a deux valeurs représentant le numéro de client et le numéro de facture et chacun des vecteurs représente une vente d'un article. Cela ressemblerait à ce que ceci:

[ 2 3 ]


0 commentaires

5 Réponses :


12
votes

dans le clojure, vous pouvez le faire presque de la même manière - premier appel distinct code> pour obtenir des valeurs uniques, puis utiliser compte code> pour compter les résultats:

(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]))


5 commentaires

Merci. La performance importe beaucoup depuis que la collecte de transactions est dans les 10 millions de dollars. Utilise le formulaire de boucle nécessite alors l'utilisation d'atomes ou quelque chose comme ça pour maintenir l'état entre chaque itération de la boucle? C'est la partie qui me trébuche, je pense.


@DaveKincAid: Voir ma mise à jour. Notez cependant que la complexité du temps de toutes les solutions est la même, de sorte que leur temps de fonctionnement ne diffère que par un multiplicateur constant (probablement assez petit).


C'est excellent! Merci. Après avoir posté ma question et voir votre première réponse. Je suis parti et j'ai expérimenté un peu. Voici ce que je suis venu avec. Je me demande si vous pouviez m'aider à comprendre quelles sont les différences entre votre approche et celle-ci. (Laissez [Set client (Atom # {}) Set (Atom # {})] (doseq [[[[Facture client] TXN] (Swap! Cintre de la conjoint) (Swap! Set! Facture))) [(Count (Comptoir (DEREF Customer-Set))])])


Premièrement, votre approche est impérative et, étant donné que Clojure est principalement une langue fonctionnelle, dans des cas plus sophistiqués que vous pourriez avoir des problèmes mineurs. Il est toujours préférable d'utiliser le paradigme principal de la langue, juste parce qu'il existe plus d'outils pour la programmation dans son style. Deuxièmement, vous utilisez des primitives de synchronisation, qui sont totalement inutiles ici: dans les langages fonctionnels que vous utilisez une récursion au lieu de boucles explicites (comme tandis que en java) et modifiez l'état lorsque vous passez à la prochaine étape de recur (voir argense se reproduire dans mon exemple). La synchronisation peut également être assez chère pour le système. Le reste est plus ou moins le même.


Merci pour l'explication. C'est exactement ce que j'essayais de comprendre.



4
votes

ou vous pouvez utiliser des ensembles pour gérer le désen-duping pour vous, car les ensembles peuvent avoir un maximum d'une valeur spécifique.

(def vectors '([100 2000] [100 2000] [101 2001] [100 2002]))    
[(count (into #{} (map first vectors)))  (count (into #{} (map second vectors)))]


0 commentaires

10
votes

Comme c'est souvent le cas lors de la consommation d'une séquence, Réduire est plus agréable que boucle ici. Vous pouvez simplement faire: xxx

ou, si vous êtes vraiment en transition: xxx

Les deux de ces solutions traversent l'entrée une seule fois et ils prennent beaucoup moins de code que la solution de boucle / recur.


5 commentaires

C'est très bien! Permettez-moi de jeter une ride. Ajoutez un troisième élément à chacun des vecteurs qui est le prix. Produit maintenant un vecteur qui inclut les comptes comme avant, mais ajoute également sur la somme des prix. Cela peut-il être fait de manière propre similaire?


C'est bien sûr possible et réduire sera toujours la meilleure approche, mais je ne vais pas l'écrire moi-même: p.


J'ai pris un coup de poignard à cela. Dis-moi à quel point ceci. Je crée une carte des fonctions (DEF F-MAP {0 Count 1 Count 2 (Réduire partielle +)}) puis utilisez la carte-indexée pour exécuter chaque fonction de la fonction correspondante de F-Carte. Comme ceci: (# indexé sur la carte ((((((((e)) de la carte% 1)% 2) (Réduire (Carte partielle conj) [# [] # [] []] TXN))


Il s'avère que cette solution souffle la pile avec trop de données. Et «Trop de choses» se révèle ne pas être ça beaucoup. Je reçois Stackoverflowerror à l'aide de milliers de rangées.


Oh, bon point. Vous pouvez résoudre ce problème avec (Comp DOALL (carte partielle ») .



1
votes

Voici une bonne façon de le faire avec une carte et des fonctions d'ordre supérieur: xxx


0 commentaires

0
votes

Aussi d'autres solutions aux belles ci-dessus ceux mentionnés:

(mappe (Compt Vector distinct) [100 2000] [100 2000] [101 2001] [100 2002])

Autre écrit avec thread-Dernière macro:

(- >> '([100 2000] [100 2000] [101 2001] [100 2002]) (Appliquez le vecteur de la carte) (Carte distincte) (Compte de la carte))

Les deux rendements (2 3).


0 commentaires