Mis à jour le mardi 7 mars 2017
  • Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Méthodes (2/2)

Connectez-vous ou inscrivez-vous pour bénéficier de toutes les fonctionnalités de ce cours !

L'un de nos principaux buts dans la programmation est d'éviter la répétition du code. Si vous écrivez du code, une instruction ou un bloc plusieurs fois (ou que vous faites des copier / coller :-° ) alors vous devez savoir qu'il y a quelque chose qui cloche et devez essayer de réduire le code. On a déjà vu qu'on écrit une classe une seule fois, mais on peut créer n'importe quel nombre d'instances. C'est exactement la même chose pour les méthodes : on les déclare une seule fois (exemple : méthode diviseurs du chapitre précédent) et on les utilise autant qu'on veut (diviseurs(45), diviseurs(12), diviseurs(100)).

Les fonctionnalités qu'on va voir dans ce deuxième chapitre sur les méthodes rentrent dans ce but, notamment les valeurs par défaut et les boucles, qui vont nous épargner plusieurs lignes de code.

Surcharge des méthodes

Signature

On appelle signature d'une méthode son nom et la liste des types de ses arguments (dans l'ordre).
Par exemple la méthode somme3

def somme3(x: Int, y: Int, z: Int) : Int = x + y + z

est de signature :

somme3(Int, Int, Int)

D'autres exemples :

def m1(s: String) = s * 2 // m1(String)
def m2 = println(789) // m2
def m3() = println(789) // m3()
def m4(a: Double, b: Boolean, c: Char) = if (b) a else c.toDouble //m4(Double, Boolean, Char)

C'est une notion simple qui nous servira lors de la surcharge des méthodes.

Surcharge

On dit qu'une méthode m2 surcharge une autre méthode m1 si m1 et m2 ont le même nom avec des signatures différentes (en d'autres termes, si m1 et m2 ont le même nom et des listes d'arguments distinctes).
Exemple :

def somme(x: Int, y: Int) = x + y // somme(Int, Int)
def somme(x: Double, y: Double) = x + y // somme(Double, Double)

Lors de l'appel de la méthode somme, Scala utilise les types des arguments passés pour savoir quelle méthode appeler :

obj.somme(5, 6) // somme(Int, Int)
obj.somme(2.0, 1.14) // somme(Double, Double)

On peut surcharger une méthode infiniment, il suffit que toutes les méthodes aient des signatures deux à deux différentes :

def somme(x: Int, y: Int) = x + y // somme(Int, Int)
def somme(x: Double, y: Double) = x + y // somme(Double, Double)
def somme(ch1: String, ch2: String) = ch1 + ch2 // somme(String, String)
def somme(x: Int, y: Int, z: Int) = x + y + z //somme(Int, Int, Int)

Cette fonctionnalité nous épargne le coup de donner des noms ridicules à nos méthodes, comme on a fait dans le chapitre précédent avec somme2 et somme3.

Arguments par défaut

On peut donner des valeurs par défaut aux arguments des méthodes, il suffit de mettre un signe égale suivi d'un objet après le type de l'argument :

def methode(arg1: <Type1> = v1, arg2: <Type2> = v2 /*...*/)

Lors de l'appel de la méthode ci-dessus, si on ne précise pas arg2 par exemple, il sera remplacé par v2.

Je ne suis pas sûr d'avoir compris ce que t'es en train de raconter. :euh:

Comme d'habitude, vous comprendrez mieux avec un exemple concret :

def concat(s1: String = "*", x: Int = 0, y: Int = 0, s2:String = "*") = s1 + x + ", " + y + s2

La méthode « concat » fait la concaténation de deux String et de deux Int. On peut l'appeler en utilisant cinq façons différentes :

obj.concat("[", 22, 14, "]")    //retourne [22, 14]
obj.concat("(", 22, 14)        //on n'a pas précisé s2 donc il sera remplacé par "*"
                              //la méthode renvoie (22, 14 *  
obj.concat("@", 22)          // y prend la valeur 0 et s2 prend "*" ce qui donne @22, 0*
obj.concat("@@")            // retourne @@0, 0*
obj.concat                 //retourne *0, 0*

Donc la déclaration de la méthode est équivalente aux méthodes surchargées suivantes :

def concat(s1: String, x: Int, y: Int, s2:String) = s1 + x + ", " + y + s2
def concat(s1: String, x: Int, y: Int)            = concat(s1, x, y, "*")
def concat(s1: String, x: Int)                    = concat(s1, x, 0, "*")
def concat(s1: String)                            = concat(s1, 0, 0, "*")
def concat                                        = concat("*", 0, 0, "*")

Donc la version initiale de « concat » est, comme les shampoings, 5 en 1. :lol:

Allez, un dernier exemple : remplacez les méthodes « somme2 » et « somme3 » du chapitre précédent par une seule méthode « somme ».

def somme(x: Int, y: Int, z: Int = 0) = x + y + z

Les boucles

On a souvent besoin de répéter le même traitement plusieurs fois. Si on veut par exemple afficher « Bonjour » à l'écran 6 fois on doit faire :

scala> "Bonjour"
res0: java.lang.String = "Bonjour"

scala> "Bonjour"
res1: java.lang.String = "Bonjour"

scala> "Bonjour"
res2: java.lang.String = "Bonjour"

scala> "Bonjour"
res3: java.lang.String = "Bonjour"

scala> "Bonjour"
res4: java.lang.String = "Bonjour"

scala> "Bonjour"
res5: java.lang.String = "Bonjour"

C'est long et fastidieux rien que pour six affichages, que faire alors s'ils étaient 50 ? 1000 ? 1000000000 ? :o
On ne va pas s'amuser à réécrire tout ça en console. Même un copier / coller dans un fichier prendra énormément de temps.

Un autre problème qui ne peut être résolu qu'avec une boucle est l'écriture d'une méthode qui affiche tous les diviseurs d'un entier.

Écrire une méthode qui affiche les diviseurs de 6 est une chose simple :

class Diviseurs{
  def divDe6 = {
    println(if (6 % 1 == 0) "1" else "")
    println(if (6 % 2 == 0) "2" else "")
    println(if (6 % 3 == 0) "3" else "")
    println(if (6 % 4 == 0) "4" else "")
    println(if (6 % 5 == 0) "5" else "")
    println(if (6 % 6 == 0) "6" else "")
  }
}

La méthode println permet d'afficher un objet à l'écran (mais si on lui passe une instance de classe qu'on a créée, elle va afficher plein de symboles qui finissent, comme toujours, par la référence de l'objet).
Son utilisation est simple : il suffit de lui passer l'objet à afficher :

scala> println(2)
2

scala> println('D')
D

En réalité l'objet existe, mais il est caché. C'est une longue histoire que je vais expliquer dans le chapitre qui suit le TP.
O.K. ce code marche, mais si la méthode devait prendre un argument « n » de classe Int et afficher tous ses diviseurs, qu'allez-vous faire ? Rien ! On ne peut pas faire de telles méthodes sans les boucles.
Il y a deux types de boucles :

  • les boucles impératives : while et do-while ;

  • les boucles fonctionnelles : for et for-yield.

Les boucles impératives

Ce sont les deux boucles while et do-while. On va apprendre à utiliser uniquement while parce que :

  • ce qu'on peut faire avec do-while est faisable avec while ;

  • l'utilisation de while et do-while n'est pas une bonne pratique en Scala (cependant on est parfois obligé de les utiliser), mais on va l'utiliser pour le moment puisque vous n'avez pas les connaissances nécessaires pour pouvoir vous en passer (la récursivité entre autres).

La boucle while

Cette boucle s'écrit en français :

Tant Que (CONDITION) EXPRESSION

Avec :

  • CONDITION : une condition booléenne, comme celle de l'if ;

  • EXPRESSION : une expression quelconque.

Comment ça marche ?

C'est simple :

  • (*) Si CONDITION == VRAI alors Scala évalue EXPRESSION et retourne à (*) (on dit qu'on retourne au début de la boucle, c'est-à-dire au Tant Que) ;

  • Si CONDITION == FAUX on sort de la boucle (c'est-à-dire que l'expression est terminée et complète le reste du programme).

Voici un exemple : supposons qu'on a une variable mutable x, de classe Int et initialisée à 3. Prenons cette boucle Tant Que :

Tant Que (x < 6) x += 1

Lorsque Scala atteint la boucle, il suit la démarche décrite plus haut, ce qui donne :

  • la condition x < 6 est VRAI (x == 3) donc on évalue l'expression x += 1 (x désormais vaut 4) et on retourne au début de la boucle ;

  • la condition x < 6 est encore VRAI (x == 4) donc on évalue une autre fois x += 1 (x contient 5 maintenant) et on retourne au début de la boucle ;

  • la condition x < 6 est encore VRAI (x == 5) donc on évalue encore une fois l'expression x += 1 (x vaut 6) et on retourne au début de la boucle ;

  • la condition x < 6 est FAUX (x vaut 6) donc on quitte la boucle.

C'est bien ?
Passons maintenant à la syntaxe en Scala :

while (CONDITION) EXPRESSION

Je compte sur vous pour transformer l'exemple en une méthode (dans une classe, bien sûr). N'oubliez pas de définir x et de faire un test après la classe.

class Boucles {
  def boucleWhile = {
    var x = 3
    while (x < 6) x += 1
  }
}
val b = new Boucles
b.boucleWhile

Et la sortie console :

defined class Boucles
b: Boucles = Boucles@2387dr8

scala>

Du calme, les gars. >_ Je vous ai déjà dit que Scala n'affiche rien dans un seul cas. Lequel ? Lorsque l'expression ne retourne rien (elle retourne quelque chose en fait : (), la seule instance de Unit). La fonction retourne un bloc qui retourne à son tour sa dernière expression, qui est la boucle while. Or toutes les boucles qu'on va voir dans ce chapitre sont des expressions de classe Unit, elles ne retournent rien d'utile.
On doit donc utiliser la méthode println pour l'affichage.

Mais on ne peut mettre qu'une seule expression dans une boucle !

:o Je ne vais pas répéter la même chose dans chaque chapitre ! On n'avancera jamais de cette manière ! Les blocs, les blocs, les blocs ! :colere: N'oubliez jamais ça !

O.K., continuons, voici le code :

def boucleWhile = {
  var x = 3
  while (x < 6) {
    x += 1
    println(x)
  }
  x
}

Et puisque je ne suis pas de bonne humeur, je vous laisse tester tout seuls.

Les boucles fonctionnelles

Les boucles fonctionnelles sont beaucoup plus importantes et beaucoup plus puissantes que les impératives. Il y a deux boucles fonctionnelles : for et for-yield. Comme je vous l'ai dit au début, la boucle for-yield sera introduite dans le chapitre sur les collections. Pourquoi ? Parce qu'elle retourne une collection :-° (ne soyez pas impatients :pirate: ).
Aussi, la boucle for peut vous paraître un peu bizarre (même si vous avez déjà programmé en C ou en Java), vous n'avez pas des connaissances suffisantes pour que je puisse vous expliquer les mécanismes internes de la boucle for. :honte: On va juste apprendre à l'utiliser, tout le fonctionnement sera expliqué lorsque nous aborderons les méthodes d'ordre supérieur (joli nom, n'est-ce pas :zorro: ?).

La boucle for (Pour)

La syntaxe de la boucle Pour est :

Pour i de MIN à MAX Faire EXPRESSION

Avec :

  • MIN et MAX deux Int ;

  • i : variable spéciale appelée variable de boucle (vous pouvez lui donner n'importe quel nom valide) ;

  • EXPRESSION : n'importe quelle expression.

Voici comment elle fonctionne :

  • si MIN > MAX, on sort de la boucle directement (c'est-à-dire qu'on ne fait rien) ;

  • sinon, i prend la valeur MIN et on évalue EXPRESSION :

    • (*) i prend i + 1 (on dit qu'on incrémente i) ;

    • si i > MAX on sort de la boucle ;

    • sinon on évalue EXPRESSION et on retourne à (*).

On va faire des tests en console pour voir clairement ce qui se passe, voici la syntaxe d'une boucle for en Scala :

for (i <- MIN to MAX) EXPRESSION

Commençons par cet exemple simple :

class Boucles {
  def boucleFor = for (i <- 1 to 6) println(i)
}

val b = new Boucles
b.boucleFor
b: Boucles = Boucles@145dg6
1
2
3
4
5
6

Je vous explique ce qui se passe.
On a MIN < MAX (1 < 6) donc i prend l'objet 1, on évalue le println, ensuite i prend l'objet 2, on évalue le println ainsi de suite jusqu'à ce que i égale MAX (6). On incrémente i, elle devient supérieure à MAX donc on quitte la boucle.
Sympa cette boucle. ^^

Écrivez une méthode qui prend en argument un Int « n » et qui affiche « Vive le SDZ » n fois. Appelez la méthode pour n = 3 et n = 20.

class Boucles {
  def afficheSDZ(n : Int) = for (i <- 1 to n) println("Vive le SDZ")
}

val b = new Boucles
println("Premier Appel : ")
b.afficherSDZ(3)
println("Deuxième Appel : ")
b.afficherSDZ(20)
b: Boucles = Boucles@25k132
"Premier Appel"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"

"Deuxième Appel"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
"Vive le SDZ"
Les conditions

Implémentons la méthode qui affiche les diviseurs d'un entier à l'aide d'une boucle for :

def diviseurs(n: Int) = for(i <- 1 to n) if(n % i == 0) println(i) else println("")

Le problème de ce code est le else inutile qu'on doit mettre (au passage, on peut écrire println tout court) et on aura aussi des retours à la ligne non nécessaires. Dans des cas pareils, lorsque seul l'if nous intéresse, on peut mettre la partie if dans la boucle :

def diviseurs(n: Int) = for (i <- 1 to n ; if (n % i == 0)) println(i)

L'expression if (n % i == 0) est appelée condition de boucle. On peut remplacer les parenthèses par des accolades et mettre le code sur plusieurs lignes :

def diviseurs(n: Int) = for{i <- 1 to n 
                            if(n % i == 0)} println(i)

On peut mettre autant de conditions de boucle qu'on veut.

for imbriquées et générateurs

On dit qu'on a deux boucles for imbriquées si une boucle est à l'intérieur de l'autre (on parle aussi de while imbriquées, de if imbriquées) :

for (i <- 1 to 2)
  for (j <- 1 to 2)
    println(i + " " + j)

Ici, EXPRESSION est une autre boucle for qui affiche i et j. L'inférence des « ; » ne pose pas de problèmes, je vous ai dit que Scala ne met pas un « ; » s'il y a une expression non complète, qui est ici la boucle.
Rien de compliqué :

1 1
1 2
2 1
2 2
  • i prend la valeur 1 et on évalue la boucle interne :

    • j prend la valeur 1 et on affiche 1 1.

    • j prend la valeur 2 et on affiche 1 2.

    • On sort de la boucle interne.

  • i prend la valeur 2 et on évalue la boucle interne :

    • j prend la valeur 1 et on affiche 2 1,

    • j prend la valeur 2 et on affiche 2 2,

    • on sort de la boucle interne,

  • on sort de la boucle externe.

Scala nous donne un raccourci pour ne pas avoir à écrire deux boucles :

for (i <- 1 to 2; j <- 1 to 2) println(i + " " + j)

On peut aussi faire autrement :

for {
  i <- 1 to 2
  j <- 1 to 2
} println(i + " " + j)

L'expression « i <- 1 to 2 » est appelée générateur. On peut mettre n'importe quel nombre de générateurs dans la boucle.

On va s'arrêter là pour ce chapitre, il commence à devenir trop long. :-°

Exercices

Exercice 1

Énoncé

Est-ce que cette déclaration est permise :

def m(x: Int) = x * x        // retourne un Int
def m(x: Int) = "123 " * x  // retourne un String
Indications

Examinez l'appel de la méthode, par exemple m(2).

Solution

Ceci est impossible. Le type de retour ne fait pas partie de la signature. ;) En plus, si cela était possible, lors de l'appel à m(2), que va retourner la méthode ? 4 ou « 123 123 » ?

Exercice 2

Énoncé

Expliquez cette remarque à l'aide d'un exemple :

Indications

Prenez l'exemple de la méthode concat.

Solution

Supposons qu'on peut laisser Scala prendre les arguments par défaut de x et y mais pas de s2, on aurait pu faire :

concat("[",2,"]")

Que va remplacer « 2 » ici ? x ou y ? C'est ambigu donc c'est impossible de faire ceci en Scala (en fait, si, avec les paramètres nommés que je ne vais pas présenter dans ce tuto).
Pour savoir comment vous pouvez appeler une méthode à arguments par défaut il suffit de suivre le schéma que j'ai fait pour « concat » (à savoir enlever un argument de la fin de la liste d'arguments à chaque fois).

Exercice 3

Énoncé

Combien de boucles a-t-on en Scala ?

Indications

Comptez !

Solution

Quatre : while, do-while, for et for-yield.

Exercice 4

Énoncé

Écrivez une méthode qui affiche les 10 premiers multiples d'un entier passé en argument (en commençant par 0).

Indications

Boucle for et println.

Solution
def f(n: Int) = for(i <- 0 to 10) println(i * n)

On a fini notre découverte des méthodes. Vous avez maintenant les connaissances nécessaires pour faire de petits programmes en console.
Dans le prochain chapitre on va pratiquer un peu le langage avec un mini-projet où vous allez créer un petit jeu de Mario en console. ;)

Exemple de certificat de réussite
Exemple de certificat de réussite