Scala

2014-09-02 21 views
9

içinde ortak öğeler içeren Kümeler kümelerini birleştirme Scala'da bir işlev uygulamak istiyorum; bir Kümeler Kümesi verildiğinde, bir veya daha fazla ortak öğe içeren Kümeyi içeren herhangi bir Küme birleştirilir.Scala

Yani, örneğin, verilen:

def mergeSets(sets: Set[Set[Int]]): Set[Set[Int]] = ??? 

val sets = Set(Set(1,2), Set(2,3), Set(3,7), Set(8,10)) 
val mergedSets = mergeSets(sets) 

mergedSets Set içerecektir (Set (1,2,3,7), Set (8,10))

güzel ne olurdu, Mümkün olduğunda verimli ve işlevsel, Scala'da bunu yapmanın yolu?

cevap

6

Bunu yapmanın en etkili yolu değişken yapıları kullanacak olan, ancak işlevsel bir şekilde istedi, bu yüzden buraya:

sets.foldLeft(Set.empty[Set[Int]])((cum, cur) => { 
    val (hasCommon, rest) = cum.partition(_ & cur nonEmpty) 
    rest + (cur ++ hasCommon.flatten) 
}) 

+0

:

Burada ile geldi budur. Bunu düzeltmek için düzenledim ve denediğim bir test vakasında çalışıyorum :) –

+0

@Paul Thx, ve aslında düzeltdiğim bir hata vardı. Ayrıca düşünürüm - kullanımdan kaldırıldı, bu yüzden filtre olarak değiştirilmediNot – samthebest

+1

'bölümü 'kesimleri ve diğerleriyle kümelere bölmek için' bölümü' kullanabilir misiniz? Biraz daha açık olabilir mi? –

0

Bu (Test edilmedi, bu kullanarak telefon yazdım) muhtemelen samthebest en cevabın sadece bir çeşididir, ancak çeşitli uğruna:

def mergeSets(sets: Set[Set[Int]]): Set[Set[Int]] = { 
    def hasIntersect(set: Set[Int]): Boolean = 
     sets.count(set.intersect(_).nonEmpty) > 1 

    val (merged, rejected) = sets partition hasIntersect 
    Set(merged.flatten, rejected.flatten) 
    } 
+0

Verilenler Ayarla (Set (1, 2), Set (2, 3), Set (4, 5), Set (5, 6)) 'fonksiyonunuz' Set (Set (1, 2, 3, 4) , 5, 6)) '' ancak istenen sonuç 'Set (Set (1, 2, 3), Set (4, 5, 6))' dır. Sağ? Benzer şekilde, tüm ayrık kümeler bir kümeye birleştirilir. – samthebest

+0

Ah, sorunu daha sonra anladığımızı görüyorum. Okuduğum şekilde çıktı, Set olmalıdır (allMembersFromSetsWithDuplicates, membersOfSetsWithoutDuplicates). Oysa bağlı kümelerin gruplandırılması olarak okursunuz. – rompetroll

+0

Eğer OP daha karmaşık bir örnek verebilirse iyi olur (örneğin Set (Set (1,2), Set (2,3), Set (3,7), Set (8,10),) Set (5,6), Set (6,9)) 'ama yorumladım @ samthebest'in yolu –

1

bir ölçüde samthebest cevabı ruhu içinde var versiyonu, ama daha az derinden deyimsel (tasarım gereği). Fonksiyonel programlamaya yeni olanlar için daha ulaşılabilir olabilir. (Onu biz böyle güzel bir problemin dışında her şeyi sıkmak gerektiğini görünüyor.)

def mergeSets(sets: Set[Set[Int]]): Set[Set[Int]] = { 
    if (sets.isEmpty) { 
    Set.empty[Set[Int]] 
    } else { 
    val cur = sets.head 
    val merged = mergeSets(sets.tail) 
    val (hasCommon, rest) = merged.partition(_ & cur nonEmpty) 
    rest + (cur ++ hasCommon.flatten) 
    } 
} 

Ancak, aşağıdaki alternatif kuyruk özyinelemeli ve belki de samthebest cevabını anlamak için daha yumuşak bir yolu sağlayan olma avantajına sahiptir:

def mergeSets(cum: Set[Set[Int]], sets: Set[Set[Int]]): Set[Set[Int]] = { 
    if (sets.isEmpty) { 
    cum 
    } else { 
    val cur = sets.head 
    val (hasCommon, rest) = cum.partition(_ & cur nonEmpty) 
    mergeSets(rest + (cur ++ hasCommon.flatten), sets.tail) 
    } 
} 

def mergeSets(sets: Set[Set[Int]]): Set[Set[Int]] = 
    mergeSets(Set.empty[Set[Int]], sets) 

Bunlardan hiçbirini üstün olarak iddia etmiyorum: sadece öğrenme araçları olarak yararlıdır.

1

Samthebest'in özlü çözümü, sadeliği ve zarafetiyle çok tatmin edici, ancak çok sayıda setle çalışıyorum ve hala işlevsel ve iyi işlevsel tarzda yazılmış daha performanslı bir çözüme ihtiyacım var.

Her biri 10 elementli 10,000 set için (rastgele seçilen 0 - 750.000 arası), samthebest'in ters çözümü bilgisayarımda ortalama ~ 30 saniye sürdü, benim çözümüm ise ortalama olarak 400 ms aldı.

kimse herhangi bir yenilik görebiliyorsanız

I tarzı bakımından yapabilir (herkes merak durumda, yukarıdaki set kardinallikleri için çıkan kümesi ~ 26 elementlerin ortalama her biriyle, ~ 3600 setleri içerir) veya performans, lütfen bana bildirin! (Filtresinde Pars içinde _ sahip olamaz) derlemek etmez

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 
    } 
}