Suche…


Liste sortieren

Angenommen, die folgende Liste kann auf verschiedene Arten sortiert werden.

val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")

Das Standardverhalten von sorted() ist die Verwendung von math.Ordering , was für Strings zu einer lexographischen Sortierung führt:

names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)

sortWith können Sie Ihre eigene Bestellung über eine Vergleichsfunktion sortWith :

names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)

sortBy können Sie eine Transformationsfunktion bereitstellen:

//A set of vowels to use
val vowels = Set('a', 'e', 'i', 'o', 'u')

//A function that counts the vowels in a name
def countVowels(name: String) = name.count(l => vowels.contains(l.toLower))

//Sorts by the number of vowels
names.sortBy(countVowels)
//result is: List(Kathryn, Beth, Serin, Allie, Alana)

Sie können eine Liste oder eine sortierte Liste jederzeit umkehren, indem Sie Folgendes verwenden:

names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)

Listen können auch mit der Java-Methode java.util.Arrays.sort und ihrem Scala-Wrapper scala.util.Sorting.quickSort sortiert werden

java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)

Diese Methoden können die Leistung beim Sortieren größerer Sammlungen verbessern, wenn die Konvertierung der Sammlung und das Unboxing / Boxing vermieden werden können. Weitere Informationen zu den Leistungsunterschieden finden Sie in der Scala Collection, sortiert, sortWith und sortBy Performance .

Erstellen Sie eine Liste mit n Kopien von x

Verwenden Sie die Füllmethode, um eine Sammlung von n Kopien eines Objekts x zu erstellen. In diesem Beispiel wird eine List . Dies kann jedoch mit anderen Sammlungen funktionieren, für die fill sinnvoll ist:

// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)

Liste und Vektor-Spickzettel

Es ist jetzt eine bewährte Methode, Vector anstelle von List da die Implementierungen eine bessere Leistung bieten. Performance-Eigenschaften finden Sie hier . Vector kann überall verwendet werden, wo List verwendet wird.

Listenerstellung

List[Int]()         // Declares an empty list of type Int
List.empty[Int]     // Uses `empty` method to declare empty list of type Int
Nil                 // A list of type Nothing that explicitly has nothing in it

List(1, 2, 3)       // Declare a list with some elements
1 :: 2 :: 3 :: Nil  // Chaining element prepending to an empty list, in a LISP-style

Nimm das Element

List(1, 2, 3).headOption // Some(1)
List(1, 2, 3).head       // 1

List(1, 2, 3).lastOption // Some(3)
List(1, 2, 3).last       // 3, complexity is O(n)

List(1, 2, 3)(1)         // 2, complexity is O(n)
List(1, 2, 3)(3)         // java.lang.IndexOutOfBoundsException: 4

Elemente voranstellen

0 :: List(1, 2, 3)       // List(0, 1, 2, 3)

Elemente anhängen

List(1, 2, 3) :+ 4       // List(1, 2, 3, 4), complexity is O(n)

Listen verbinden (verketten)

List(1, 2) ::: List(3, 4) // List(1, 2, 3, 4)
List.concat(List(1,2), List(3, 4)) // List(1, 2, 3, 4)
List(1, 2) ++ List(3, 4)  // List(1, 2, 3, 4)

Gemeinsame Operationen

List(1, 2, 3).find(_ == 3)                     // Some(3)
List(1, 2, 3).map(_ * 2)                       // List(2, 4, 6)
List(1, 2, 3).filter(_ % 2 == 1)               // List(1, 3)
List(1, 2, 3).fold(0)((acc, i) => acc + i * i) // 1 * 1 + 2 * 2 + 3 * 3 = 14
List(1, 2, 3).foldLeft("Foo")(_ + _.toString)  // "Foo123"
List(1, 2, 3).foldRight("Foo")(_ + _.toString) // "123Foo"

Kartensammlung Cheatsheet

Beachten Sie, dass dies die Erstellung einer Sammlung vom Typ Map betrifft, die sich von der map Methode unterscheidet.

Kartenerstellung

Map[String, Int]() 
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()

Eine Map kann für die meisten Operationen als eine Sammlung von tuples werden, wobei das erste Element der Schlüssel und das zweite Element der Wert ist.

val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap                               // Map(a -> 1, b -> 2, c -> 3)

Element abrufen

val m = Map("a" -> 1, "b" -> 2, "c" -> 3)

m.get("a")  // Some(1)
m.get("d")  // None
m("a")      // 1
m("d")      // java.util.NoSuchElementException: key not found: d

m.keys      // Set(a, b, c)
m.values    // MapLike(1, 2, 3)

Element (e) hinzufügen

Map("a" -> 1, "b" -> 2) + ("c" -> 3)               // Map(a -> 1, b -> 2, c -> 3)
Map("a" -> 1, "b" -> 2) + ("a" -> 3)               // Map(a -> 3, b -> 2)
Map("a" -> 1, "b" -> 2) ++ Map("b" -> 3, "c" -> 4) // Map(a -> 1, b -> 3, c -> 4)

Gemeinsame Operationen

Bei Operationen, bei denen eine Iteration über eine Karte stattfindet ( map , find , forEach usw.), sind die Elemente der Sammlung tuples . Der Funktionsparameter kann entweder die Tupel-Accessoren ( _1 , _2 ) oder eine Teilfunktion mit einem Case-Block verwenden:

m.find(_._1 == "a")  // Some((a,1))
m.map {
  case (key, value) => (value, key)
}                    // Map(1 -> a, 2 -> b, 3 -> c)
m.filter(_._2 == 2)  // Map(b -> 2)
m.foldLeft(0){
  case (acc, (key, value: Int)) => acc + value
}                    // 6

Karte und Filter über eine Sammlung

Karte

'Mapping' über eine Sammlung hinweg verwendet die map , um jedes Element dieser Sammlung auf ähnliche Weise zu transformieren. Die allgemeine Syntax lautet:

val someFunction: (A) => (B) = ???
collection.map(someFunction)

Sie können eine anonyme Funktion bereitstellen:

collection.map((x: T) => /*Do something with x*/)

Multiplikation ganzzahliger Zahlen mit zwei

// Initialize 
val list = List(1,2,3)
// list: List[Int] = List(1, 2, 3)

// Apply map
list.map((item: Int) => item*2)
// res0: List[Int] = List(2, 4, 6)

// Or in a more concise way
list.map(_*2)
// res1: List[Int] = List(2, 4, 6)

Filter

filter wird verwendet, wenn Sie bestimmte Elemente einer Sammlung ausschließen oder ausfiltern möchten. Wie bei map nimmt die allgemeine Syntax eine Funktion an, aber diese Funktion muss einen Boolean :

val someFunction: (a) => Boolean = ???
collection.filter(someFunction)

Sie können eine anonyme Funktion direkt bereitstellen:

collection.filter((x: T) => /*Do something that returns a Boolean*/)

Paarnummern prüfen

val list = 1 to 10 toList
// list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// Filter out all elements that aren't evenly divisible by 2
list.filter((item: Int) => item % 2==0)
// res0: List[Int] = List(2, 4, 6, 8, 10)

Weitere Karten- und Filterbeispiele

case class Person(firstName: String,
                  lastName: String,
                  title: String)

// Make a sequence of people
val people = Seq(
  Person("Millie", "Fletcher", "Mrs"),
  Person("Jim", "White", "Mr"),
  Person("Jenny", "Ball", "Miss") )


// Make labels using map
val labels = people.map( person =>
  s"${person.title}. ${person.lastName}"
)

// Filter the elements beginning with J
val beginningWithJ = people.filter(_.firstName.startsWith("J"))

// Extract first names and concatenate to a string
val firstNames = people.map(_.firstName).reduce( (a, b) => a + "," + b )

Einführung in die Scala-Sammlungen

Das Scala Collections-Framework ist laut seinen Autoren einfach zu bedienen, präzise, ​​sicher, schnell und universell.

Das Framework besteht aus Scala- Merkmalen , die als Bausteine ​​für das Erstellen von Sammlungen konzipiert wurden. Weitere Informationen zu diesen Bausteinen finden Sie in der offiziellen Übersicht der Scala-Sammlungen .

Diese integrierten Sammlungen sind in unveränderliche und veränderliche Pakete unterteilt. Standardmäßig werden die unveränderlichen Versionen verwendet. Beim Erstellen einer List() (ohne etwas zu importieren) wird eine unveränderliche Liste erstellt.

Eine der leistungsfähigsten Funktionen des Frameworks ist die konsistente und benutzerfreundliche Oberfläche für gleichgesinnte Sammlungen. Das Summieren aller Elemente in einer Collection ist beispielsweise für Listen, Sets, Vektoren, Seqs und Arrays gleich:

val numList = List[Int](1, 2, 3, 4, 5)
numList.reduce((n1, n2) => n1 + n2)  // 15

val numSet = Set[Int](1, 2, 3, 4, 5)
numSet.reduce((n1, n2) => n1 + n2)   // 15

val numArray = Array[Int](1, 2, 3, 4, 5)
numArray.reduce((n1, n2) => n1 + n2) // 15

Diese gleichgesinnten Typen erben von der Traversable Eigenschaft.

Es ist jetzt eine bewährte Methode, Vector anstelle von List da die Implementierungen eine bessere Leistung bieten. Performance-Eigenschaften finden Sie hier . Vector kann überall verwendet werden, wo List verwendet wird.

Verfahrbare Typen

Auflistungsklassen mit der Eigenschaft " Traversable implementieren foreach und erben viele Methoden zum Ausführen allgemeiner Operationen für Auflistungen, die alle identisch funktionieren. Die häufigsten Vorgänge sind hier aufgelistet:

  • Map - map , flatMap und collect erzeugen neue Sammlungen, indem auf jedes Element der ursprünglichen Sammlung eine Funktion flatMap wird.
List(1, 2, 3).map(num => num * 2) // double every number = List(2, 4, 6)

// split list of letters into individual strings and put them into the same list
List("a b c", "d e").flatMap(letters => letters.split(" ")) // = List("a", "b", "c", "d", "e")
  • Konvertierungen - toList , toArray und viele andere Konvertierungsvorgänge ändern die aktuelle Sammlung in eine spezifischere Art der Sammlung. Dies sind normalerweise Methoden, denen 'to' vorangestellt wird, und der spezifischere Typ (dh 'toList' konvertiert in eine List ).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
  • Größeninfo - isEmpty , nonEmpty , size und hasDefiniteSize sind alle Metadaten über das Set. Dies ermöglicht bedingte Operationen für die Sammlung oder für Code, um die Größe der Sammlung zu bestimmen, einschließlich, ob sie unendlich oder diskret ist.
List().isEmpty // true
List(1).nonEmpty // true
  • Elementabruf - head , last , find und ihre Option werden verwendet, um das erste oder letzte Element abzurufen oder ein bestimmtes Element in der Auflistung zu suchen.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
  • Unterteilungsoperationen - partition , splitAt , span und groupBy die aktuelle Sammlung in verschiedene Teile groupBy .
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
  • Elementtests - exists und count sind Vorgänge, mit denen diese Sammlung forall wird, um forall , ob sie ein Vergleichselement erfüllt.
List(1, 2, 3, 4).forall(num => num > 0) // = true, all numbers are positive
List(-3, -2, -1, 1).forall(num => num < 0) // = false, not all numbers are negative

Falten

Die fold Methode durchläuft eine Sammlung, wobei ein anfänglicher Akkumulatorwert verwendet wird und eine Funktion angewendet wird, die jedes Element zum erfolgreichen Aktualisieren des Akkumulators verwendet:

val nums = List(1,2,3,4,5)
var initialValue:Int = 0;
var sum = nums.fold(initialValue){
  (accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 15 because 0+1+2+3+4+5 = 15

Im obigen Beispiel wurde eine anonyme Funktion für fold() bereitgestellt. Sie können auch eine benannte Funktion verwenden, die zwei Argumente akzeptiert. Wenn man dies in mir trägt, kann das obige Beispiel folgendermaßen umgeschrieben werden:

def sum(x: Int, y: Int) = x+ y
val nums = List(1, 2, 3, 4, 5)
var initialValue: Int = 0
val sum = nums.fold(initialValue)(sum)
println(sum) // prints 15 because 0 + 1 + 2 + 3 + 4 + 5 = 15

Das Ändern des Anfangswerts wirkt sich auf das Ergebnis aus:

initialValue = 2;
sum = nums.fold(initialValue){ 
 (accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17

Die fold Methode hat zwei Varianten - foldLeft und foldRight .

foldLeft() durchläuft von links nach rechts (vom ersten Element der Sammlung bis zum letzten in dieser Reihenfolge). foldRight() iteriert von rechts nach links (vom letzten bis zum ersten Element). fold() iteriert von links nach rechts wie foldLeft() . In der Tat ruft fold() tatsächlich foldLeft() .

def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)

fold() , foldLeft() und foldRight() geben einen Wert zurück, der denselben Typ hat wie der ursprüngliche Wert. Im Gegensatz zu foldLeft() und foldRight() kann der für fold() angegebene Anfangswert jedoch nur vom selben Typ oder einem Supertyp des Typs der Sammlung sein.

In diesem Beispiel ist die Reihenfolge nicht relevant. Sie können fold() in foldLeft() oder foldRight() und das Ergebnis bleibt gleich. Die Verwendung einer auftragsempfindlichen Funktion ändert die Ergebnisse.

Im Zweifelsfall ziehen Sie foldLeft() vor foldRight() . foldRight() ist weniger performant.

Für jeden

foreach ist unter den Iteratoren der Sammlungen ungewöhnlich, da es kein Ergebnis zurückgibt. Stattdessen wird auf jedes Element eine Funktion angewendet, die nur Nebeneffekte hat. Zum Beispiel:

scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)

scala> x.foreach { println }
1
2
3

Die an foreach gelieferte Funktion kann einen beliebigen Rückgabetyp haben, das Ergebnis wird jedoch verworfen . In der Regel wird foreach verwendet, wenn Nebenwirkungen erwünscht sind. Wenn Sie Daten transformieren möchten, sollten Sie map , filter , ein for comprehension oder eine andere Option verwenden.

Beispiel für das Verwerfen von Ergebnissen

def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing

Reduzieren

Die Methoden reduceRight reduce() , reduceLeft() und reduceRight (ähnlich reduceLeft() ähneln Falten. Die zum Reduzieren übergebene Funktion nimmt zwei Werte und liefert einen dritten Wert. Wenn Sie mit einer Liste arbeiten, sind die ersten beiden Werte die ersten beiden Werte in der Liste. Das Ergebnis der Funktion und der nächste Wert in der Liste werden dann erneut auf die Funktion angewendet und ergeben ein neues Ergebnis. Dieses neue Ergebnis wird mit dem nächsten Wert der Liste usw. angewendet, bis keine Elemente mehr vorhanden sind. Das Endergebnis wird zurückgegeben.

val nums = List(1,2,3,4,5)
sum = nums.reduce({ (a, b) => a + b })
println(sum) //prints 15

val names = List("John","Koby", "Josh", "Matilda", "Zac", "Mary Poppins")

def findLongest(nameA:String, nameB:String):String = {
  if (nameA.length > nameB.length) nameA else nameB
}

def findLastAlphabetically(nameA:String, nameB:String):String = {
  if (nameA > nameB) nameA else nameB
}

val longestName:String = names.reduce(findLongest(_,_))
println(longestName) //prints Mary Poppins

//You can also omit the arguments if you want
val lastAlphabetically:String = names.reduce(findLastAlphabetically)
println(lastAlphabetically) //prints Zac

Es gibt einige Unterschiede in der Funktionsweise der Reduktionsfunktionen im Vergleich zu den Falzfunktionen. Sie sind:

  1. Die Reduzierfunktionen haben keinen anfänglichen Speicherwert.
  2. Reduktionsfunktionen können bei leeren Listen nicht aufgerufen werden.
  3. Reduktionsfunktionen können nur den Typ oder den Supertyp der Liste zurückgeben.


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow