Une brève introduction aux collections Scala avec les listes

Les collections de Scala sont riches et puissantes. Elles font, sans doute, partie des “features” qui rendent Scala attrayant pour les développeurs. Elles démontrent aussi toute la puissance que Scala offre à ceux des librairies ou des “frameworks”. En effet les collections de Scala ne sont pas contruites dans le langage lui-même; c’est une librairie que vous auriez vous pu écrire avec ce que Scala vous offre. Dans ce billet il ne s’agit de rentrer dans les détails de fabrication des collections de Scala mais plutôt de nous placer du point de vue d’un utilisateur des collections. J’espère qu’à la fin vous verrez que Scala ne mérite pas toujours sa réputation de langage difficile d’accès. A mon humble avis un grand nombre de “features” avancées de Scala ne doivent pas être exposées à l’utilisateur final d’une API mais plutôt exploitées par les développeurs de librairies ou de frameworks. Le contraire risquerait de nuire à l’adoption de ce magnifique langage. Fermons cette longue parathèse et passons au sujet de ce billet: les collections de Scala vues au travers des listes.

Veuillez me signaler toute incorrection dans ce billet!

Pour exécuter les bouts de code de ce billet vous pouvez utiliser directement l’interpréteur interactif de Scla - REPL (Read-Evaluate-Print Loop).

Construction

La construction d’une liste en Scala est on ne peut plus simple. Par exemple comment créer une liste de noms.

scala> val names = List("Kandi", "Sylvie", "Mamadou", "Toto", "John", "Jane")
names: List[java.lang.String] = List(Kandi, Sylvie, Mamadou, Toto, John, Jane)

Au regard de la sortie de l’interpréteur interactif REPL on se rend compte Scala on a fait l’inférence de type sur notre de noms et en a conclu qu’il s’agit d’une liste dont les éléments sont de type java.lang.String. Ceci va aider à l’écriture du code plus concis qu’on peut avec un langage comme Java. Voyons si cela marche avec une liste de nombres! Voici le résultat:

scala> val numbers = List(2, 4, 7, 0, 7, -4, 2, 3, 45, 5, 3)
numbers: List[Int] = List(2, 4, 7, 0, 7, -4, 2, 3, 45, 5, 3)

Et ça marche aussi! Le type Int a été inféré.

Traverser une liste

Nous pouvons traverser une liste et appliquer une opération à chacun de ses éléments grâce à la méthode foreach dont voici la signature:

//List foreach method signature
def foreach (f: (A) ⇒ Unit) : Unit

Pour utiliser cette méthode il suffit de lui passer une fonction qui sera application à chaque élément de la liste. Dans le code cela donne:

val numbers = List(2, 4, 7, 0, 7, -4, 2, 3, 45, 5, 3)
//For ways to do the same thing
numbers.foreach {x => println(x)}
numbers.foreach {println(_)}
numbers.foreach {println}
//With this version we have less noise
numbers foreach println

//Remember functions are first-class citizens in Scala
var display = (x: Any) => println("Elemn = " + x)
numbers foreach display

Filtrage

Les opérations de filtrage sont assez simple avec les éléments de Scala. Supposons par exemple que nous souhaitons seulement récupérer les éléments positifs d’une liste de nombres. Nous ferons cela avec les méthode List.filter dont voici la signature.

def filter (p: (A) ⇒ Boolean) : List[A]

Cette méthode applique la fonction passée en argument aux éléments de la liste. On obtient en retour une liste constituée des éléments de la liste initiale satisfaisant la condition posée par la fonction.

val numbers = List(2, 4, -7, 0, 7, -4, 2, 3, 45, -5, 3)
//Filter negative numbers
numbers.filter(x => x >= 0)
//This gives List[Int] = List(2, 4, 0, 7, 2, 3, 45, 3) as result

Voici comment obtenir une liste constituée seulement des éléments pairs de notre liste de nombres:

val numbers = List(2, 4, -7, 0, 7, -4, 2, 3, 45, -5, 3)
//Filter odd numbers
numbers.filter(x => x % 2 == 0)
//This gives List[Int] = List(2, 4, 0, -4, 2) as result

Pendant qu’on y est voici une autre méthode de List qui est du même acabit que filter:

def filterNot (p: (A) ⇒ Boolean) : List[A]

Cette méthode fait l’opposé de ce que fait la méthode List.filter.

La méthode List.map Cette méthode application une transformation, une fonction, à chaque élément d’une collection. Elle peut potentiellement changer le type des éléments de la collection, par exemple en passant d’une collection de String à une collection d’éléments de type Int. Le listing suivant donne quelques cas d’usage de cette méthode. Suivez les commentaires dans le code.

val names = List("Kandi", "Jean", "Boubacar", "Larry", "Hawa")
//Obtenir une liste contenant les tailles des éléments de notre liste initiale.
names.map {n => n.length}
//Résultat = List[Int] = List(5, 4, 8, 5, 4)

val numbers = List(2, 4, -7, 0, 7, -4, 2, 3, 45, -5, 3)

//Obtenir une liste contenant le double des éléments d'une liste donnée.
numbers.map {x => 2 * x}
//Ecrit autrement cela donne:
numbers.map { _ * 2}
//Résultat = List(4, 8, -14, 0, 14, -8, 4, 6, 90, -10, 6)

Partitionnons notre liste Si nous souhaitions “partionner” une collection suivant un critère donné? Cela est possible grâce à la méthode List.partition. Voici comment partionner une liste de nombres en nombres positifs et négatifs. Dans le code cela donne…

val numbers = List(2, 4, -7, 0, 7, -4, 2, 3, 45, -5, 3)
//Partage la liste en deux sous-listes: l'une contenant les nombres positifs
//et l'autre contenant des éléments négatifs.
numbers.partition(x => x < 0)
//Résultat = (List(-7, -4, -5),List(2, 4, 0, 7, 2, 3, 45, 3))

La méthode foldLeft

Pour finir cette découverte des collections de Scala découvrons une dernière méthode de List. Voici sa signature

def foldLeft [B] (z: B)(f: (B, A) ⇒ B) : B

B est le type du résultat final et z est la valeur initiale utilisée pour l’opération. La fonction f est appliquée d’abord sur l’élément le plus à gauche de la liste et la valeur initiale puis sur le deuxième sur le deuxième élément le plus à gauche et le résultat de la première application, et ainsi de suite. Passons à la pratique. Faisons la somme d’une liste de nombres positifs en utilisant la méthode foldLeft. Dans ce cas la valeur initiale est 0.

val numbers = List(2, 4, 1, 2, 3, 3)
//Somme les éléments de notre liste.
//La valeur initiale est 0 car nous faisons une addition.
numbers.foldLeft(0){(x, y) => x + y}
//Résultat = 15

Pour finir Les boucles sont omniprésentes lorsqu’on travaille avec les collections en Scala contrairement à Java. L’aspect fonctionnel de Scala rend pratique la manipulation des collections. Cet article n’était qu’une mise en bouche; une façon pour moi de vous donner envie d’explorer davantage les collections de Scala. C’est un sujet vaste que je continue à explorer. J’apprécierai le signalement de toute erreur présente dans cet article!