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
Vector
plutôt que laList
car les implémentations ont de meilleures performances. Les caractéristiques de performances peuvent être trouvées ici .Vector
peut être utilisé partout où laList
est 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
Vector
plutôt que laList
car les implémentations ont de meilleures performances. Les caractéristiques de performances peuvent être trouvées ici .Vector
peut être utilisé partout où laList
est 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
,flatMap
etcollect
produisent 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
,toArray
et 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
,size
ethasDefiniteSize
sont 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
,find
et leurs variantesOption
permettent 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
filter
tail
slice
drop
opé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
,span
etgroupBy
divisent 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
,forall
etcount
sont 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
(:\
),reduceLeft
etreduceRight
sont 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.