Vous ne vous en êtes peut-être pas rendu compte, mais vous avez créé un logiciel de gestion de ferme ! En tout cas, il y en a un qui a bien compris l'opportunité, et qui n'arrête pas de fixer la barre plus haut ! Eh, oui c'est Joe, notre geek converti ! Voilà ce qu'il vous propose :
from: joe@lafermedejoe.fr
subject : Wow !
Vraiment je suis épaté par ce que tu as pu réaliser. Tu ne te rends pas compte à quel point tu m'aides dans mon travail. Voilà plusieurs jours que j'utilise ton programme, et je te propose un petit point d'amélioration. Pour les activités que j'enregistre, je ne récupère pas toujours les quantités par défaut que tu proposes. Je parle de celles-ci, du coup :
🐄 J’ai trait mes vaches
🌾 J’ai moissonné
🐑 J’ai tondu mes moutons
Est-ce que ton programme pourrait me permettre de rentrer moi-même le nombre de bidons de lait, bottes de blé et pelotes de laine que je récupère ?
Merci beaucoup ! À bientôt,
Joe
Ajoutez des paramètres aux fonctions
Pour satisfaire les envies de Joe, nous allons découvrir une nouvelle possibilité des fonctions : les paramètres !
Pour l'instant, vous savez qu'une fonction, schématiquement, ça ressemble à ceci :
Une fonction rassemble des instructions derrière un nom, et renvoie une valeur de retour. Avec les paramètres, on va pouvoir faire passer un cap à nos fonctions, pour qu'elle ressemble désormais à ceci :
On va pouvoir passer des paramètres à une fonction pour modifier son comportement.
Définissez un paramètre
Par exemple, si on veut créer une fonction qui ajoute 2 à un nombre, on écrira :
func ajouteDeux(nombre: Int) -> Int {
return nombre + 2
}
Mis à part la partie nombre: Int
, vous êtes normalement en terrain conquis. On a une fonction appelée ajouteDeux
, créée avec le mot-clé func
, et qui retourne une valeur de type Int
. Si ce n'est pas très clair, pas de souci, rafraîchissez-vous les idées 2 chapitres en arrière !
Parlons maintenant de ce fameux nombre: Int
. C'est ainsi que l'on définit un paramètre dans une fonction : comme pour une variable, on lui donne un nom, ici nombre
, et un type, ici Int
, séparés par les deux-points. On n’est pas très loin de la déclaration d'une variable :
var nombre: Int
Sauf que là, à l'intérieur des parenthèses de la fonction, on n’écrit pas le mot-clé var
car ce n'est pas une variable, mais un paramètre de la fonction. Ensuite, comme une variable, on peut utiliser le paramètre à l'intérieur de la fonction :
return nombre + 2
Ici on retourne donc le nombre + 2.
OK, mais il vaut quoi le nombre ?!
Il vaut ce que vous voulez ! On va préciser ce qu'il vaut au moment de l'appel de la fonction ! Par exemple, si on veut ajouter 2 au nombre 12, on écrira :
ajouteDeux(nombre: 12) // La fonction retourne 14.
On écrit donc le nom du paramètre nombre
, les deux points :
` et la valeur que l'on souhaite, 12
.
On a donc une super fonction qui peut rajouter 2 à n'importe quel nombre !
ajouteDeux(nombre: 3) // 5
ajouteDeux(nombre: -2) // 0
ajouteDeux(nombre: 25) // 27
C'est clair ? On va pouvoir monter en difficulté !
Ajoutez plusieurs paramètres
On peut définir plusieurs paramètres pour une fonction, autant que l'on souhaite, même !
Par exemple, si on souhaite faire la somme de trois nombres, on peut écrire :
func somme(a: Int, b: Int, c: Int) -> Int {
return a + b + c
}
somme(a: 2, b: 1, c: 3) // 6
On sépare simplement les paramètres avec des virgules ,
.
Utilisez des étiquettes
Swift a une façon assez particulière de traiter les fonctions pour les rendre les plus lisibles possible. Avec Swift, vous l'aviez compris, on veut être clair ! Et on va le faire avec les étiquettes !
Le plus simple, c'est que je vous montre avec un exemple :
func allerAuCinema(nom: String) {
print("Je vais au cinéma avec " + nom)
}
allerAuCinema(nom: "Bob") // affiche "Je vais au cinéma avec Bob"
Cette fonction affiche "Je vais au cinéma avec" et le nom de la personne de votre choix. Le problème, c'est que la phrase : Aller au cinéma nom Bob, elle ne veut rien dire !
Alors on pourrait écrire :
func allerAuCinema(avec: String) {
print("Je vais au cinéma avec " + avec)
}
allerAuCinema(avec: "Bob") // affiche "Je vais au cinéma avec Bob"
Évidemment, là ça sonne mieux ! Mais du coup, notre paramètre s'appelle avec
. Ce qui n'est pas très clair vu qu'il contient le nom de la personne. On préférait quand il s'appelait nom
.
On est donc coincé. Doit-on privilégier la clarté de l'appel de la fonction ou du paramètre ?
Euh...
Vous êtes comme moi. Vous ne savez pas quoi choisir ! Heureusement, c'est inutile, car avec Swift on peut faire les deux, comme ceci :
func allerAuCinema(avec nom: String) {
print("Je vais au cinéma avec " + nom)
}
allerAuCinema(avec: "Bob") // affiche "Je vais au cinéma avec Bob"
J'ai rajouté dans la définition le mot avec
. C'est l'étiquette du paramètre nom
. Du coup, dans l'appel de la fonction, on va pouvoir utiliser uniquement l'étiquette avec
. Cela rend l'appel plus clair sans toucher au nom du paramètre.
Tout ça pour ça ?
Vous me connaissez, j'aime la beauté. Je n’y peux rien, plus c'est beau, plus je suis heureux. Mais je ne suis pas le seul ! Vous aussi, vous aimez ça, mais vous ne le savez pas encore . Il est très important pour la clarté d'un code de faire très attention à la lisibilité de votre code. Sinon vous pourrez vous y perdre. Donc je vous suggère d'utiliser les étiquettes autant que besoin !
Si vous n'écrivez pas d'étiquette, c'est le nom du paramètre qui est choisi par défaut. Mais techniquement, il y a donc bel et bien une étiquette à l'appel de la fonction. Sachez qu'il existe un moyen de n'avoir pas d'étiquette du tout en utilisant le caractère _
.
func saluer(_ nom: String) {
print("Bonjour " + nom)
}
saluer("Jean")
Ici il est plus clair de ne pas avoir d'étiquette du tout, donc on écrit _
à la place de l'étiquette dans la déclaration, pour spécifier qu'on veut utiliser ce paramètre sans étiquette.
Parce qu'on aime quand c'est beau, voici un petit tableau pour vous résumer tout ça :
func nomDeLaFonction(étiquette paramètre: Type) { instructions... } nomDeLaFonction(étiquette: valeur) | L'étiquette est précisée. Elle est utilisée dans l'appel de la fonction. |
func nomDeLaFonction(paramètre: Type) { instructions... } nomDeLaFonction(paramètre: valeur) | L'étiquette n'est pas précisée. Le nom du paramètre est utilisé comme étiquette par défaut dans l'appel. |
func nomDeLaFonction(_paramètre: Type) { instructions... } nomDeLaFonction(valeur) | On utilise le caractère Il n'y a pas d'étiquette dans l'appel. |
func multiplier(_ a: Int, et b: Int) -> Int {
return a * b
}
multiplier(2, et: 3) // 6
N’oubliez pas les types
Vous ne pensiez pas que je n'allais pas vous faire un petit laïus sur les types !
Comme pour le reste, on peut utiliser n'importe quel type pour une variable : String, Int, Optionnel, Tableau, Booléen, absolument tout !
Pour autant, on ne fait pas n'importe quoi avec les types des paramètres, on ne peut pas utiliser une valeur qui n'est pas du bon type pour le paramètre !
Ceci par exemple énerve un peu Swift…
func ajouteDeux(nombre: Int) {
return nombre + 2
}
ajouteDeux(nombre: "deux ?") // ERREUR !
On ne mélange pas les torchons et les serviettes. Ça vous paraît évident ? Ça ne l'est pas tant que ça dans d'autres langages. Heureusement pour nous, Swift est très rigoureux et tant mieux ! Ça nous évite de faire des bêtises.
À vous de jouer
En utilisant tout ça, on va pouvoir transformer notre programme pour que Joe puisse entrer exactement le nombre de bidons de lait, bottes de blé et pelotes de laine qu'il récupère.
Alors reprenons tranquillement ce qui a changé. Tout d'abord, on a transformé les trois fonctions pour leur ajouter un paramètre quantity
:
func milkCows(retrieving quantity: Int) {
barn["milk"]! += quantity
}
func harvest(retrieving quantity: Int) {
barn["wheat"]! += quantity
}
func mowSheep(retrieving quantity: Int) {
barn["wool"]! += quantity
}
J'ai rajouté l'étiquette retrieving (qui veut dire en récupérant, en anglais). Ainsi les fonctions se lisent aisément : tondre les moutons en récupérant telle quantité.
Ensuite j'ai ajouté une fonction un peu sophistiquée pour lire une quantité :
func readQuantity(of type: String) -> Int? {
// On demande la quantité désirée
print("Combien de \(type) avez vous récupéré ?")
// On convertit et on renvoie la réponse
if let line = readLine() {
if let quantity = Int(line) {
return quantity
}
}
// Si la valeur n'a pas pu être interprétée, on le dit
print("Je n'ai pas compris.")
return nil
}
Plusieurs choses à préciser là-dessus :
Cette fonction prend comme paramètre type , qui décrit le type de marchandises concerné. Cela permet de poser la question : Combien de pelotes de laine avez-vous récupérées ? par exemple.
Cette fonction retourne un optionnel. Ce qui est assez logique, car si le fermier répond par exemple "trois" en toutes lettres, on ne pourra pas transformer cela en type
Int
.Souvenez-vous lorsque l'instruction
return
est appelée, l'exécution de la fonction s'arrête. C'est la raison pour laquelle j'ai écrit après le premier return :print("Je n'ai pas compris.")
Cette instruction ne sera effectuée que si le premier return n'est pas appelé. Donc uniquement si la conversion en type Int n'a pas été effectuée. Et c'est ce qu'on veut.
On doit toujours renvoyer quelque chose si un type de renvoi est précisé pour une fonction. Donc ici, comme je n'ai rien à renvoyer, je renvoie simplement nil. Ce qui est autorisé, car mon type de renvoi est optionnel.
return nil
Ensuite, on n'a plus qu'à utiliser tout cela ensemble ici :
case "3": // Traire les vaches
if let quantity = readQuantity(of: "🍼") {
milkCows(retrieving: quantity)
}
case "4": // Moissonner
if let quantity = readQuantity(of: "🌾") {
harvest(retrieving: quantity)
}
case "5": // Tondre les moutons
if let quantity = readQuantity(of: "⚪️") {
mowSheep(retrieving: quantity)
}
On remarque que nos fonctions avec paramètre se lisent facilement grâce aux étiquettes. Par ailleurs, notre fonction readQuantity
renvoie un optionnel comme readLine
, et donc s'utilise de la même manière. On n'est pas dépaysé, c'est élégant !
Vous n'avez pas forcément eu les mêmes idées que moi et c'est tant mieux ! N'hésitez pas à comparer les solutions et à choisir ce que vous préférez. L'essentiel étant la clarté !
En résumé
Les fonctions peuvent accepter des paramètres, qui permettent de configurer la fonction.
Un paramètre, c'est comme une variable qui n'existe qu'à l'intérieur de la fonction. Comme toute variable, elle a un nom et un type définis comme ceci :
func nomDeLaFonction(param1: Type1, param2: Type2) {
// Ici je peux utiliser param1 et param2 comme des variables
}
La valeur d'un paramètre n'est pas définie lors de la définition de la fonction, mais lors de son appel :
nomDeLaFonction(param1: valeur1, param2: valeur2)
Les paramètres peuvent avoir des étiquettes qui permettent un appel plus lisible de la fonction.
Une fonction qui a un paramètre d'un certain type ne peut pas être appelée avec une valeur d'un type différent.
Une fonction peut avoir 0, 1 ou plusieurs paramètres.
Bravo ! Maintenant que vous êtes devenu un expert avec les fonctions, je vous invite à passer au dernier quiz afin de clôturer cette partie dans les règles de l’art !