Clojure - Zählen von eindeutigen Werten aus Vektoren in einer seq

7

Ich bin etwas neu in Clojure. Ich kann mir nicht vorstellen, wie ich etwas tun soll, das so einfach zu sein scheint. Ich kann es einfach nicht sehen. Ich habe eine Reihe von Vektoren. Nehmen wir an, jeder Vektor hat zwei Werte, die Kundennummer und Rechnungsnummer darstellen, und jeder der Vektoren repräsentiert einen Verkauf eines Artikels. So würde es ungefähr so ​​aussehen:

%Vor%

Ich möchte die Anzahl der eindeutigen Kunden und einmaligen Rechnungen zählen. Also sollte das Beispiel den Vektor

erzeugen %Vor%

In Java oder einer anderen imperativen Sprache würde ich jeden der Vektoren im Seq durchlaufen, die Kundennummer und Rechnungsnummer zu einem Satz hinzufügen, dann die Anzahl der Werte in jedem Satz zählen und sie zurückgeben. Ich kann den funktionalen Weg nicht sehen, dies zu tun.

Danke für die Hilfe.

EDIT: Ich hätte in meiner ursprünglichen Frage angeben sollen, dass der Seq der Vektoren in den 10 Millionen von Millionen ist und tatsächlich mehr als nur zwei Werte hat. Also möchte ich nur einmal durch den Seq gehen und diese einzigartigen Zählungen (und auch einige Summen) auf diesem einen Durchlauf durch die Seq berechnen.

    
Dave Kincaid 11.07.2012, 12:33
quelle

5 Antworten

11

In Clojure können Sie es fast genauso machen - rufen Sie zuerst distinct an, um eindeutige Werte zu erhalten, und verwenden Sie dann count , um die Ergebnisse zu zählen:

%Vor%

Beachten Sie, dass Sie hier zuerst eine Liste von ersten und zweiten Elementen von Vektoren erhalten (Map-First / Second-Vektoren) und dann jeweils getrennt arbeiten und somit zweimal über die Sammlung iterieren. Wenn die Leistung eine Rolle spielt, können Sie dasselbe mit der Iteration tun (siehe loop form oder tail recursion) und setzen, genau wie in Java. Um die Leistung weiter zu verbessern, können Sie auch transients verwenden. Obwohl für Anfänger wie dich würde ich zuerst mit distinct empfehlen.

UPD. Hier ist Version mit Schleife:

%Vor%

Wie Sie sehen können, brauchen Sie keine Atome oder so etwas. Zuerst übergeben Sie den Status an jede nächste Iteration (wiederholter Aufruf). Zweitens verwenden Sie Transienten, um temporäre veränderbare Sammlungen zu verwenden (lesen Sie mehr über Transienten für Details) und vermeiden Sie so jedes Mal die Erstellung eines neuen Objekts.

UPD2. Hier ist die Version mit reduce für die erweiterte Frage (mit Preis):

%Vor%

Hier halten wir Zwischenergebnisse in einem Vektor [custs invs total] , entpacken, verarbeiten und packen sie jedes Mal wieder in einen Vektor. Wie Sie sehen können, ist das Implementieren einer solchen nichttrivialen Logik mit reduce schwieriger (sowohl Schreiben als auch Lesen) und erfordert noch mehr Code (in loop ed Version reicht es aus, einen weiteren Parameter für Preis-zu-Schleife-Argumente hinzuzufügen). Also stimme ich @ammaloy zu, dass für einfachere Fälle reduce besser ist, aber komplexere Dinge mehr Konstrukte auf niedriger Ebene erfordern, wie zB loop/recur pair.

    
ffriend 11.07.2012, 12:44
quelle
9

Wie es oft der Fall ist, wenn eine Sequenz konsumiert wird, ist reduce hier netter als loop . Sie können einfach tun:

%Vor%

Oder, wenn Sie Transienten wirklich mögen:

%Vor%

Beide Lösungen durchlaufen die Eingabe nur einmal und sie benötigen viel weniger Code als die loop / recur-Lösung.

    
amalloy 11.07.2012 18:21
quelle
4

Oder Sie könnten Sets verwenden, um das Deduping für Sie zu handhaben, da Sets maximal einen beliebigen spezifischen Wert haben können.

%Vor%     
Base 11.07.2012 12:57
quelle
1

Hier ist ein guter Weg, dies mit einer Karte und Funktionen höherer Ordnung zu tun:

%Vor%     
mikera 11.07.2012 22:45
quelle
0

Auch andere Lösungen zu den oben genannten netten:

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

Andere geschrieben mit Thread-Last-Makro:

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

beide zurück (2 3).

    
ateymuri 03.09.2015 17:14
quelle

Tags und Links