2013-08-15 6 views
9

Ich versuche derzeit, einige Java-Code in Scala-Code zu konvertieren. Die Herausforderung besteht darin, sicherzustellen, dass der konvertierte Scala-Code im Vergleich zu dem ursprünglichen Java-Code nicht sehr ineffizient ist. Für z.B. Wenn Sie versuchen, den folgenden Code zu konvertieren:Einzelne Iteration => Mehrere Ausgabe Sammlungen von Java nach Scala

Da Java auf Mutation beruht, sieht dieser Code logisch aus. Aber, jetzt möchte ich dies zu seinem Scala-Äquivalent ohne umwandeln die Sammlung mehrmals (in diesem Fall 3 Mal).

Ich kann natürlich wandelbar verwenden List aus der Scala Bibliothek und erreichen das gleiche wie von Java gemacht, aber frage mich, ob es möglich war, mehrere Sammlungen aus einer bestimmten Sequenz/Sammlung in einer funktionalen/Scala Weg ohne zu erzeugen Iterieren über die Sammlung für n Zeiten wo n ist die Anzahl der Kriterien. Danke im Voraus!

Antwort

5

Eine rein funktionale und unveränderliche Art und Weise wäre es, eine generische Funktion zu haben, die Sammlungen in Eimer, durch Prädikat trennt:

case class Person(name: String, age: Int, gender: String) 

def bucketsByPredicate(people: Seq[Person], predicates: Seq[Person => Boolean]) = { 
    people.foldLeft(predicates.map(predicate => 
    (predicate, List.empty[Person]) 
)) { case (predicates, person) => 
     predicates.map { case (predicate, members) => 
     (predicate, if(predicate(person)) person :: members else members) 
     } 
    }.map(_._2) 
} 

Dann wird ein Beispiel für die Verwendung sein könnte:

val olderThan60 = (p: Person) => p.age >= 60 
val male = (p: Person) => p.gender == "m" 
val Seq(olderThan60People, malePeople) = bucketsByPredicate(people, Seq(olderThan60, male)) 
+1

+1, aber wow, das sieht komplex aus. Ist diese Art von Sachen im Scala-Land ziemlich alltäglich? – sasuke

+6

Ein pragmatischerer Ansatz besteht darin, in der Methode veränderbare Auflistungen wie mutable.ListBuffer zu verwenden, dann result() aufzurufen und die unveränderbaren Auflistungen zurückzugeben. Das ist nicht rein funktional, sondern lesbarer und intuitiver. –

+0

Yup, ich akzeptiere diese Antwort, weil dies das ist, wonach ich gefragt habe, aber ich werde mit dem veränderbaren Ansatz gehen. Dies ist so, weil es scheint, dass es keine Probleme geben sollte, solange die Mutation in einer Methode enthalten ist. – sasuke

3
case class Person(gender:Sex,name:String,age:Int) 

sealed trait Sex 
case object Male extends Sex 
case object Female extends Sex 

def part3(l:List[Person]) = { 
    def aux(l:List[Person],acc:(List[Person],List[Person],List[Person])) : (List[Person],List[Person],List[Person]) = l match { 
    case Nil => acc 
    case head::tail => { 
     val newAcc = (if (head.gender == Male) head :: acc._1 else acc._1, 
        if (head.age >= 60) head :: acc._2 else acc._2, 
        if (head.name startsWith "a") head :: acc._3 else acc._3) 
     aux(tail,newAcc) 
    } 
    } 
    val emptyTuple = (List[Person](),List[Person](),List[Person]()) 
    // It is (much) faster to reverse the input list and then prepend during to recursive loop. 
    // prepend is O(1), append is O(n) 
    aux(l.reverse,emptyTuple) 
} 

val (males,seniors,aNames) = part3(List(Person(Male,"abc",20),Person(Female,"def",61),Person(Male,"Nope",99))) 
println(s"males : $males \nseniors : $seniors \naNames : $aNames") 

// outputs 
// males : List(Person(Male,abc,20), Person(Male,Nope,99)) 
// seniors : List(Person(Female,def,61), Person(Male,Nope,99)) 
// aNames : List(Person(Male,abc,20)) 

(Das Listen [Person] -Tupel lässt das ziemlich hässlich aussehen, Sie möchten vielleicht einen Typalias für Ihre Ergebnisse definieren.)

3

Das ist ziemlich pragmatisch. Musterübereinstimmung über der Liste der Personen, überprüfen Sie die Bedingungen in jedem rekursiven Aufruf und fügen Sie den Ergebnissen nach Bedarf hinzu, bis die Liste erschöpft ist. Die Ergebnisse sind eine ListPerson, die in eine Tuple gestopft wird.

class Person(val name: String, val age: Integer, val gender: Character){ 
    override def toString = name + " " + age + " " + gender 
} 

val alice = new Person("Alice", 18, 'f') 
val bob = new Person("Bob", 18, 'm') 
val charlie = new Person("Charlie", 60, 'm') 
val diane = new Person("Diane", 65, 'f') 
val fred = new Person("Fred", 65, 'm') 

def filterPersons(persons: List[Person]) = { 
    import scala.collection.mutable.{ListBuffer => LB} 
    def filterPersonsR(persons: List[Person], males: LB[Person], anames: LB[Person], seniors: LB[Person]): Tuple3[LB[Person], LB[Person], LB[Person]] = persons match { 
    case Nil => (males, anames, seniors) 
    case h :: t => { 
     filterPersonsR(t, if(h.gender == 'm') males += h else males, if(h.name.startsWith("A")) anames += h else anames, if(h.age >= 60) seniors += h else seniors) 
    } 
    } 
    filterPersonsR(persons, LB(), LB(), LB()) 
} 

Testing ...

scala> filterPersons(List(alice, bob, charlie, diane, fred)) 
res25: (scala.collection.mutable.ListBuffer[Person], scala.collection.mutable.ListBuffer[Person], scala.collection.mutable.ListBuffer[Person]) = (ListBuffer(Bob 18 m, Charlie 60 m, Fred 65 m),ListBuffer(Alice 18 f),ListBuffer(Charlie 60 m, Diane 65 f, Fred 65 m)) 

scala> res25._1 
res26: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Bob 18 m, Charlie 60 m, Fred 65 m) 

scala> res25._2 
res27: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Alice 18 f) 

scala> res25._3 
res28: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Charlie 60 m, Diane 65 f, Fred 65 m) 
2
import scala.collection.immutable.List 
import scala.collection.mutable.ListBuffer 

case class Person(name: String, age: Int, gender: String) 

def partition(persons: List[Person], predicates: List[Person => Boolean]): List[List[Person]] = { 

    val bufs = List.fill(predicates.size)(new ListBuffer[Person]) 

    persons.foreach { person => 
     (0 until predicates.size).foreach{ i => 
      if (predicates(i)(person)) bufs(i) += person 
     } 
    } 

    bufs map (_.toList) 
} 

val alice = new Person("Alice", 18, "f") 
val bob = new Person("Bob", 18, "m") 
val charlie = new Person("Charlie", 60, "m") 
val diane = new Person("Diane", 65, "f") 
val fred = new Person("Fred", 65, "m") 

val olderThan60 = (p: Person) => p.age >= 60 
val male = (p: Person) => p.gender == "m" 
val nameStartsWith = (p: Person) => p.name.startsWith("A") 

println(partition(List(alice, bob, charlie, diane, fred), List(olderThan60, male, nameStartsWith))) 

diese Lösung ist nicht nur funktional, sondern einfacher. Es gibt viele Situationen, in denen veränderbare Sammlungen (wie ListBuffer) besser funktionieren. Stellen Sie nur sicher, dass Sie den veränderbaren Status nicht außerhalb der Funktion oder Klasse verlieren. die Vorteile der Lesbarkeit werden den Verlust der Reinheit lohnen.

+0

+1, danke. Ist Prädikate (i) nicht lineare Zeit in Ihrem Fall? – sasuke

+0

ja, Prädikate (i) braucht lineare Zeit, aber ich denke nicht, dass die Größe der Prädikate groß genug wäre, um die Leistung zu beeinflussen. Wenn Sie sich wirklich darum kümmern, sollten Sie stattdessen Array verwenden – Septem

+0

Ah, ich verstehe.Eine letzte Frage: Wie entscheiden sich Leute zwischen der Verwendung von "ListBuffer" oder "ArrayBuffer" oder "MutableList", wenn es darum geht, veränderbare Strukturen in einer bestimmten Funktion zu verwenden? – sasuke

1

Ein anderes Beispiel mit foldLeft, aber vielleicht ein bisschen einfacher zu lesen.

//setup test data 
case class Person(gender: Char, age: Int, name: String) 
val persons = List(Person('m', 30, "Steve"), Person('m', 15, "John"), Person('f', 50, "Linda")) 

//function that takes a list, a person and a predicate. 
//returning same list if predicate is false, else new list with person added 
def inBucket(f: Person => Boolean, p: Person, list: List[Person]) = if (f(p)) p :: list else list 

//what to do in each fold step. produces a new intermediate tuple of lists every time 
def bucketize(lists: (List[Person], List[Person], List[Person]), next: Person): (List[Person], List[Person], List[Person]) = { 
    val (males, females, adults) = lists; 
    (inBucket(_.gender == 'm', next, males), 
    inBucket(_.gender == 'f', next, females), 
    inBucket(_.age >= 18, next, adults)) 
} 
val (males, females, adults) = persons.foldLeft((List[Person](), List[Person](), List[Person]()))(bucketize) 

//you can use males, females and adults now, they are of type List[Person] 
+0

+1 für neue Perspektive. – sasuke

Verwandte Themen