Scala Language
Collections
Recherche…
Trier une liste
En supposant la liste suivante, nous pouvons trier une variété de manières.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
Le comportement par défaut de math.Ordering sorted() consiste à utiliser math.Ordering , qui pour les chaînes génère un tri lexographique :
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith vous permet de fournir votre propre commande en utilisant une fonction de comparaison:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy vous permet de fournir une fonction de transformation:
//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)
Vous pouvez toujours inverser une liste ou une liste triée en utilisant `reverse:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Les listes peuvent également être triées à l'aide de la méthode Java java.util.Arrays.sort et de son wrapper Scala scala.util.Sorting.quickSort
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Ces méthodes peuvent améliorer les performances lors du tri de collections plus volumineuses si les conversions de collections et le déballage / la boxe peuvent être évités. Pour une discussion plus détaillée sur les différences de performances, consultez l'article Scala Collection trié, trié et triBy Performance .
Créer une liste contenant n copies de x
Pour créer une collection de n copies d'un objet x , utilisez la méthode de remplissage . Cet exemple crée une List , mais cela peut fonctionner avec d'autres collections pour lesquelles le fill sens:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Cheatsheet Liste et Vecteur
Il est désormais recommandé d'utiliser le
Vectorplutôt que laListcar les implémentations ont de meilleures performances. Les caractéristiques de performances peuvent être trouvées ici .Vectorpeut être utilisé partout où laListest utilisée.
Création de liste
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
Prendre élément
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
Éléments complémentaires
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Ajouter des éléments
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Rejoindre (concaténer) les listes
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)
Opérations communes
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"
Carte cheatsheet
Notez que cela concerne la création d'une collection de type
Map, distincte de la méthode demap.
Création de carte
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Une carte peut être considérée comme une collection de tuples pour la plupart des opérations, où le premier élément est la clé et le second la valeur.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Get element
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)
Ajouter des éléments
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)
Opérations communes
Dans les opérations où une itération sur une carte se produit ( map , find , forEach , etc.), les éléments de la collection sont des tuples . Le paramètre de fonction peut soit utiliser les accesseurs de tuple ( _1 , _2 ), soit une fonction partielle avec un bloc de casse:
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
Carte et filtre sur une collection
Carte
«Mappage» dans une collection utilise la fonction de map pour transformer chaque élément de cette collection de la même manière. La syntaxe générale est la suivante:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
Vous pouvez fournir une fonction anonyme:
collection.map((x: T) => /*Do something with x*/)
Multiplication des nombres entiers par deux
// 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)
Filtre
filter est utilisé lorsque vous souhaitez exclure ou filtrer certains éléments d'une collection. Comme avec map , la syntaxe générale prend une fonction, mais cette fonction doit renvoyer une valeur Boolean :
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
Vous pouvez fournir une fonction anonyme directement:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Vérification des numéros de paire
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)
Plus d'exemples de carte et de filtre
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 )
Introduction aux collections de la Scala
Selon ses auteurs , le cadre Scala Collections est conçu pour être facile à utiliser, concis, sûr, rapide et universel.
Le cadre est constitué de traits Scala conçus pour constituer des blocs de création de collections. Pour plus d'informations sur ces blocs de construction, lisez l'aperçu des collections Scala .
Ces collections intégrées sont séparées dans les packages immuables et mutables. Par défaut, les versions immuables sont utilisées. Construire une List() sans rien importer construira une liste immuable .
L’une des fonctionnalités les plus puissantes de l’infrastructure est l’interface cohérente et conviviale à travers des collections aux vues similaires. Par exemple, la somme de tous les éléments d’une collection est la même pour les listes, les ensembles, les vecteurs, les seqs et les tableaux:
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
Ces types partageant les mêmes idées héritent du trait Traversable .
Il est désormais recommandé d'utiliser le
Vectorplutôt que laListcar les implémentations ont de meilleures performances. Les caractéristiques de performances peuvent être trouvées ici .Vectorpeut être utilisé partout où laListest utilisée.
Types transversables
Classes de collection qui ont le Traversable trait mettre en œuvre foreach et héritera de nombreuses méthodes pour effectuer des opérations communes aux collections, toutes fonctionnent de manière identique. Les opérations les plus courantes sont répertoriées ici:
- Map -
map,flatMapetcollectproduisent de nouvelles collections en appliquant une fonction à chaque élément de la collection originale.
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")
- Conversions -
toList,toArrayet de nombreuses autres opérations de conversion transforment la collection actuelle en un type de collection plus spécifique. Ce sont généralement des méthodes précédées de «to» et du type plus spécifique (c.-à-d. «ToList» converti en uneList).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Informations sur la taille -
isEmpty,nonEmpty,sizeethasDefiniteSizesont toutes des métadonnées sur l'ensemble. Cela permet aux opérations conditionnelles sur la collection ou au code de déterminer la taille de la collection, notamment si elle est infinie ou discrète.
List().isEmpty // true
List(1).nonEmpty // true
- Récupération d'éléments -
head,last,findet leurs variantesOptionpermettent de récupérer le premier ou le dernier élément ou de rechercher un élément spécifique dans la collection.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Les
filtertailslicedropopérations de récupération de sous-collection -filter,tail,slice,drop, et d' autres opérations permettent de choisir les parties de la collection pour fonctionner sur plus loin.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Opérations de subdivision -
partition,splitAt,spanetgroupBydivisent la collection actuelle en différentes parties.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Les tests d'éléments -
exists,foralletcountsont des opérations utilisées pour vérifier cette collection pour voir si elle satisfait un prédicat.
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
- Folds -
foldLeft(/:foldRight,foldRight(:\),reduceLeftetreduceRightsont utilisés pour appliquer des fonctions binaires aux éléments successifs de la collection. Allez ici pour des exemples de pliage et allez ici pour réduire les exemples .
Plier
La méthode fold itération sur une collection, en utilisant une valeur d'accumulateur initiale et en appliquant une fonction qui utilise chaque élément pour mettre à jour l'accumulateur avec succès:
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
Dans l'exemple ci-dessus, une fonction anonyme a été fournie pour fold() . Vous pouvez également utiliser une fonction nommée qui prend deux arguments. En prenant ceci dans mon, l'exemple ci-dessus peut être ré-écrit ainsi:
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
Changer la valeur initiale affectera le résultat:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
La méthode fold a deux variantes - foldLeft et foldRight .
foldLeft() itère de gauche à droite (du premier élément de la collection au dernier dans cet ordre). foldRight() itère de droite à gauche (du dernier élément au premier élément). fold() itère de gauche à droite comme foldLeft() . En fait, fold() appelle en réalité foldLeft() interne.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold() , foldLeft() et foldRight() retourneront une valeur qui a le même type que la valeur initiale. Cependant, contrairement à foldLeft() et foldRight() , la valeur initiale donnée à fold() ne peut être que du même type ou d'un sur-type du type de la collection.
Dans cet exemple, l'ordre n'est pas pertinent, vous pouvez donc changer fold() en foldLeft() ou foldRight() et le résultat restera le même. L'utilisation d'une fonction sensible à l'ordre modifie les résultats.
En cas de doute, préférez foldLeft() sur foldRight() . foldRight() est moins performant.
Pour chaque
foreach est inhabituel parmi les itérateurs de collections en ce sens qu'il ne renvoie pas de résultat. Au lieu de cela, il applique une fonction à chaque élément qui n'a que des effets secondaires. Par exemple:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
La fonction fournie à foreach peut avoir n'importe quel type de retour, mais le résultat sera ignoré . Typiquement, foreach est utilisé lorsque les effets secondaires sont souhaitables. Si vous souhaitez transformer des données, utilisez une map , un filter , une for comprehension ou une autre option.
Exemple de rejet des résultats
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Réduire
Les méthodes reduceRight reduce() , reduceLeft() et reduceRight sont similaires aux plis. La fonction passée à réduire prend deux valeurs et en génère une troisième. Lorsque vous utilisez une liste, les deux premières valeurs sont les deux premières valeurs de la liste. Le résultat de la fonction et la valeur suivante dans la liste sont ensuite réappliqués à la fonction, produisant un nouveau résultat. Ce nouveau résultat est appliqué avec la valeur suivante de la liste et ainsi de suite jusqu'à ce qu'il n'y ait plus d'éléments. Le résultat final est renvoyé.
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
Il existe des différences dans le fonctionnement des fonctions de réduction par rapport aux fonctions de pliage. Elles sont:
- Les fonctions de réduction n'ont pas de valeur d'accumulateur initiale.
- Les fonctions de réduction ne peuvent pas être appelées sur des listes vides.
- Les fonctions Réduire ne peuvent que renvoyer le type ou le sur-type de la liste.