Samthebests knappe Lösung ist sehr befriedigend in ihrer Einfachheit und Eleganz, aber ich arbeite mit einer großen Anzahl von Sets und brauchte eine performantere Lösung, die immer noch unveränderlich und in einem guten funktionalen Stil geschrieben ist.
Für 10.000 Sets mit je 10 Elementen (zufällig ausgewählte Werte von 0 bis 750.000) benötigte samtests knappe Lösung durchschnittlich ~ 30 Sekunden auf meinem Computer, während meine Lösung im Durchschnitt ~ 400 ms dauerte.
(Falls jemand fragt mich, der resultierende Satz für den obigen Satz Mächtigkeiten enthält ~ 3600 Sets, mit einem Durchschnitt von ~ 26 Elementen jeweils)
Wenn jemand irgendwelche Verbesserungen sehen kann ich in Bezug auf Stil machen könnte oder Leistung, lass es mich wissen!
Hier ist, was ich kam mit:
val sets = Set(Set(1, 2), Set(2, 3), Set(4, 5))
Association.associate(sets) => Set(Set(1, 2, 3), Set(4, 5))
object Association {
// Keep track of all current associations, as well as every element in any current association
case class AssociationAcc[A](associations: Set[Set[A]] = Set.empty[Set[A]], all: Set[A] = Set.empty[A]) {
def +(s: Set[A]) = AssociationAcc(associations + s, all | s)
}
// Add the newSet to the set associated with key A
// (or simply insert if there is no such key).
def updateMap[A](map: Map[A, Set[A]], key: A, newSet: Set[A]) = {
map + (key -> (map.getOrElse(key, Set.empty) ++ newSet))
}
// Turn a Set[Set[A]] into a map where each A points to a set of every other A
// it shared any set with.
//
// e.g. sets = Set(Set(1, 2), Set(2, 3), Set(4, 5))
// yields: Map(1 -> Set(2), 2 -> Set(1, 3), 3 -> Set(2),
// 4 -> Set(5), 5 -> Set(4))
def createAssociationMap[A](sets: Set[Set[A]]): Map[A, Set[A]] = {
sets.foldLeft(Map.empty[A, Set[A]]) { case (associations, as) =>
as.foldLeft(associations) { case (assoc, a) => updateMap(assoc, a, as - a) }
}
}
// Given a map where each A points to a set of every A it is associated with,
// and also given a key A starting point, return the total set of associated As.
//
// e.g. with map = Map(1 -> Set(2), 2 -> Set(1, 3), 3 -> Set(2),
// 4 -> Set(5), 5 -> Set(4))
// and key = 1 (or 2 or 3) yields: Set(1, 2, 3).
// with key = 4 (or 5) yields: Set(4, 5)
def getAssociations[A](map: Map[A, Set[A]], key: A, hit: Set[A] = Set.empty[A]): Set[A] = {
val newAssociations = map(key) &~ hit
newAssociations.foldLeft(newAssociations | hit + key) {
case (all, a) => getAssociations(map, a, all)
}
}
// Given a set of sets that may contain common elements, associate all sets that
// contain common elements (i.e. take union) and return the set of associated sets.
//
// e.g. Set(Set(1, 2), Set(2, 3), Set(4, 5)) yields: Set(Set(1, 2, 3), Set(4, 5))
def associate[A](sets: Set[Set[A]]): Set[Set[A]] = {
val associationMap = createAssociationMap(sets)
associationMap.keySet.foldLeft(AssociationAcc[A]()) {
case (acc, key) =>
if (acc.all.contains(key)) acc
else acc + getAssociations(associationMap, key)
}.associations
}
}
nicht kompiliert (Sie können nicht _ in Pars im Filter haben). Ich habe es bearbeitet, um das zu beheben, und es funktioniert auf dem einen Testfall, den ich versuchte :) –
@Paul Thx, und es gab tatsächlich einen Fehler, den ich gerade reparierte. Auch ich denke - ist veraltet, so änderte es zu filterNot – samthebest
Kannst du 'partition' benutzen, um' cum' in die Sätze mit Kreuzungen und den Rest zu teilen? Könnte ein bisschen klarer sein? –