Bravo ! Notre programme commence à avoir une chouette tête !
J'ai eu des nouvelles de Joe. Il n'a pas eu le temps de passer vous voir, mais sachez qu'il est très reconnaissant de ce que vous faites pour lui. Il a déjà acheté ses palmes...
Je vais vous embêter, mais je crois qu'on peut faire mieux que ça. Et on va découvrir comment les fonctions nous permettent de rendre notre code magnifique et extrêmement clair !
Enlevez la mocheté
Notre code a commencé à pas mal s'étoffer, et du coup il est de moins en moins facile à lire pour un débutant. Si vous le montrez à vos amis non programmeurs, je suis à peu près certain qu'ils auront du mal à le lire.
Et puis même entre programmeurs, il est très important d'avoir un code d'une très grande clarté. Prenons un exemple :
// On calcule la taille de la grange
var barnSize = 0
for (goods, count) in barn {
barnSize += count
}
Avec ce code, nous calculons la taille de la grange, c'est-à-dire le nombre de produits qu'elle contient. Et si au lieu d'écrire tout ceci, nous écrivions simplement :
calculateBarnSize
Ce serait beau quand même ! Je vous vois déjà saliver !
On va pouvoir le faire avec les fonctions, c'est fou ça ! Alors, allons-y !
Découvrez les fonctions
Une fonction, c'est une brique d'instructions que l'on regroupe derrière un nom. Pour calculateBarnSize
, ça ressemble à ça :
var barnSize = 0
for (goods, count) in barn {
barnSize += count
}
On va donc attribuer un nom à plusieurs lignes du programme. Cela permet d'avoir une plus grande clarté du code.
Par exemple, écrivons une fonction qui affiche un message d'accueil :
func souhaiterLaBienvenue() {
print("Bienvenue dans mon super programme, je suis ravi de vous voir !")
}
En détail, pour écrire une fonction, il faut :
Écrire le mot-clé
func
.Écrire le nom de la fonction, ici
souhaiterLaBienvenue
. On peut choisir ce qu'on veut, du moment que cela décrit clairement ce que fait la fonction.Écrire des parenthèses
()
.Le contenu de la fonction s'écrit entre accolades
{}
.
Si on exécute ce programme, il ne va rien se passer. Ne soyez pas déçu, c'est juste qu'on n'a fait que définir la fonction, on ne l'a pas encore appelée.
Pour appeler une fonction, on écrit le nom de la fonction suivi de parenthèses :
// Je définis la fonction
func souhaiterLaBienvenue() {
print("Bienvenue dans mon super programme, je suis ravi de vous voir !")
}
// J'appelle la fonction
souhaiterLaBienvenue()
Cette fois-ci le programme va bien me renvoyer :
Bienvenue dans mon super programme, je suis ravi de vous voir !
Il y a donc deux étapes dans la vie d'une fonction :
Sa définition : on associe un nom à une ou plusieurs lignes de code.
Son appel : le contenu de la fonction est effectivement exécuté.
Appréhendez la portée d'une variable
Essayons d'écrire notre fonction calculateBarnSize
. On va donc placer les lignes correspondantes dans une fonction comme ceci :
func calculateBarnSize() {
// On calcule la taille de la grange
var barnSize = 0
for (goods, count) in barn {
barnSize += count
}
}
Vous pouvez placer cette fonction juste avant la boucle while. Il nous suffit maintenant de remplacer le code correspondant dans notre boucle while
.
while money < price {
// Joe nourrit les vaches tous les jours
money -= 4
calculateBarnSize()
if barnSize >= 500 {
Je vous invite à le faire. Allez-y ! Ça marche ? Super ! Alors, passons à la...
Hop hop hop ! Pas si vite, ça ne marche pas ton truc j'ai une erreur qui dit : error: use of unresolved identifier 'barnSize'
.
J'avoue, je vous ai un peu testé. En effet, on ne peut pas écrire ça comme ça directement. Quelle est donc cette erreur ? Elle indique que la variable barnSize
n'existe pas.
Mais si elle existe ! Elle est déclarée à l'intérieur de la fonction calculateBarnSize
!
Oui mais... ce n'est pas suffisant ! Et pour comprendre cela, il faut que je vous explique ce qu'est la portée d'une variable.
En fait, une variable n'existe que dans le contexte dans lequel elle a été déclarée. Un contexte a pour limite des accolades. Par exemple, prenons le programme suivant :
var age = 12
while age < 21 {
var message = "Je suis mineur aux USA"
if age >= 18 {
let majoriteDepuis = age - 18
message += " mais majeur en France depuis \(majoriteDepuis) an(s) !"
}
print(message)
age += 1
}
print("Enfin la majorité absolue !")
Ce petit programme démarre à l'âge 12. À chaque tour dans la boucle while, la variable age
s'incrémente d'une année. Cette variable est utilisée pour créer une phrase qui donne le statut légal de la personne chaque année. La console affiche ceci :
Je suis mineur aux USA Je suis mineur aux USA Je suis mineur aux USA Je suis mineur aux USA Je suis mineur aux USA Je suis mineur aux USA Je suis mineur aux USA mais majeur en France depuis 0 an(s) ! Je suis mineur aux USA mais majeur en France depuis 1 an(s) ! Je suis mineur aux USA mais majeur en France depuis 2 an(s) Enfin la majorité absolue !
Je vous invite à copier-coller le code pour vous assurer que vous le compreniez correctement.
Dans ce code, on a plusieurs fois des accolades qui définissent trois contextes différents :
Il y a trois variables dans ce code. Chacune est définie dans un contexte différent. Et elle est du coup disponible uniquement à l'intérieur de ce contexte. Donc dans ce schéma :
La variable
age
est définie dans le contexte 1 et est donc disponible dans les contextes 1, 2, 3 car les contextes 2 et 3 sont inclus dans le 1er.La variable
message
est définie dans le contexte 2 et est donc disponible dans les contextes 2 et 3. Mais pas le 1er.La variable
majoriteDepuis
est définie dans le contexte 3 et est donc disponible uniquement dans ce contexte.
Donc quand on écrit une variable à l'intérieur d'une fonction, la variable existe uniquement dans le contexte de cette fonction, entre les accolades. Mais pas en dehors. Voilà pourquoi le programme nous dit que la variable n'est pas définie.
Pour résumer : une variable définie entre accolades {}
n'est pas disponible en dehors de ces accolades.
Obtenez la valeur de retour
Maintenant que nous avons compris notre erreur, nous allons pouvoir la résoudre. Une façon rapide de le faire serait de sortir la déclaration var barnSize = 0
de notre fonction. Comme cela, la variable serait disponible dans le contexte le plus global, à savoir tout le fichier.
Vous pouvez essayer... Mais on va faire mieux !
Une fonction qui renvoie quelque chose
On a vu pour l'instant l'usage le plus simple d'une fonction : regrouper des lignes de code derrière un nom. Mais une fonction peut aussi renvoyer une information. Par exemple, faire un calcul et renvoyer le résultat de ce calcul.
Pour cela, il suffit de préciser à la déclaration de la fonction le type de valeur renvoyé. La fonction ressemble du coup à :
La syntaxe pour préciser le type de la valeur s'écrit comme suit :
func calculateBarnSize() -> Int {
// Implementation de la fonction
}
Si vous rajoutez seulement ceci, Swift va renvoyer une erreur. Car vous venez de déclarer une fonction qui renvoie une valeur de type Int
, mais en inspectant la fonction, Swift ne voit pas de valeur de retour ; donc il n'est pas content !
Le mot-clé return
Pour renvoyer effectivement une valeur, on va utiliser le mot-clé return
. Comme ceci :
func calculateBarnSize() -> Int {
// On calcule la taille de la grange
var barnSize = 0
for (goods, count) in barn {
barnSize += count
}
return barnSize // On retourne la taille de la grange
}
Une fois la taille de la grange calculée, on utilise le mot-clé return
pour la renvoyer. Ensuite si on appelle la fonction calculateBarnSize()
, elle va renvoyer la taille de la grange.
func renvoieLeMotBonjour() -> String {
print("Je suis affiché")
return "Bonjour"
print("Je ne suis pas affiché")
print("Moi non plus")
print("Toujours pas")
}
renvoieLeMotBonjour()
Ce petit programme affiche seulement dans la console :
Je suis affiché
Pour réparer notre programme, on va pouvoir écrire :
while money < price {
// Joe nourrit les vaches tous les jours
money -= 4
var barnSize = calculateBarnSize()
if barnSize >= 500 {
La variable barnSize
va contenir la valeur renvoyée par la fonction calculateBarnSize
. L'instruction calculateBarnSize()
se comporte d'ailleurs exactement comme une variable de type Int
. Donc on peut même écrire directement :
while money < price {
// Joe nourrit les vaches tous les jours
money -= 4
if calculateBarnSize() >= 500 {
Et voilà, c'est plus concis et plus clair comme ça !
Retournez plusieurs valeurs avec les tuples
La version précédente de Swift, Swift 4, a permis d’introduire le type tuples.
C’est quoi un tuple ?
Pour faire simple, un tuple est utilisé pour regrouper plusieurs valeurs dans une seule valeur composée. Au fait, les tuples dans Swift occupent une place entre les dictionnaires et les structures (on verra cela plus tard ! ) :
Comme les structures, ils contiennent des types de données très spécifiques.
Mais comme des dictionnaires, ils peuvent être créés à la volée.
Vous me voyez venir ?
Eh oui ! Ils sont couramment utilisés pour renvoyer plusieurs valeurs à partir d'un appel de fonction, et sont devenus très populaires dans la plupart des applications utilisées en production, car ils permettent de regrouper un ensemble de données connexes, telles que le nom d’une personne, son âge et son genre, par exemple. Il faut voir cela un peu comme un dossier où l’on souhaiterait regrouper des informations de différentes natures. L’idée c’est de se dire qu’on va s’en servir pour pouvoir retourner plusieurs valeurs avec une seule fonction !
Les valeurs d'un tuple peuvent être de n'importe quel type, et n'ont pas besoin d'être du même type.
Vous pouvez créer un tuple de base comme celui-ci :
let barn = (product: "milk", quantity: 20)
Ici, nous avons un tuple avec deux valeurs, la première est une chaîne de caractères et la deuxième est un entier. Vous pouvez créer des tuples à partir d'autant de valeurs que vous le souhaitez, et à partir de n'importe quel nombre de types de données différents.
Maintenant, voyons un peu plus en détail comment cela fonctionne, et aussi à quoi cela peut bien servir concrètement !
Les tuples anonymes
Tout d’abord, regardons d’un peu plus près la syntaxe d’un tuple avec Swift :
let product = ("wheat", 50)
let productSheet = ("milk", 20, true)
Ici nous avons déclaré deux groupes pour la ferme :
Un premier avec deux valeurs, une chaîne de caractères avec un entier, que nous avons appelé product, et qui va nous permettre de savoir le nom du produit et la quantité disponible.
Un deuxième groupe tuple avec une chaîne de caractères, un booléen et un double, que nous avons appelé productSheet, qui correspond à la fiche d’un produit si l’on souhaite avoir plus de détails (par exemple ici : savoir si le produit est disponible ou non).
Ces tuples sont dit ‘anonymes’ car on ne peut accéder à leurs valeurs qu’en utilisant leur position dans le tuple :
let barn = ("milk", true, 20)
let firstElement = barn.0
let secondElement = barn.1
let thirdElement = barn.2
print(firstElement) // “milk”
print(secondElement) // true
print(thirdElement) // 20
Les tuples nommés
Pour rendre la lecture et l’écriture des tuples plus simples, il est possible de nommer les différents éléments au sein des tuples. Par exemple :
var barn = (product: “milk”, quantity: “20”, available: ”true”)
print(barn.product) // milk
Et pour faire encore plus simple, on peut tout à fait rendre les tuples accessibles à partir d’une variable :
let milk = barn.product
print(milk) // milk
OK, les tuples, c’est sympa, mais quand doit-on les utiliser, du coup ?
Vous devez les utiliser lorsque vous avez besoin de renvoyer plusieurs valeurs avec une fonction !
Voici un exemple plus concret :
// Joe récupère les informations concernant le produit du lait
func milkProduct() -> (name: String, quantity: Int) {
// On retourne deux valeurs ici : "milk" et 20
return (name: "milk", quantity: 20)
}
On constate ici que la fonction milkProduct()
ne prend aucun paramètre. Elle a plusieurs valeurs de retour, notamment le nom du produit et sa quantité.
OK, mais comment on récupère plusieurs valeurs de retour au sein d’une fonction, du coup ?
Comme vous savez déjà le faire pour récupérer la valeur de retour d’une fonction, ici c’est exactement pareil ! Il vous suffit de faire :
let result = milkProduct()
Tada ! On vient de créer un tuple en récupérant la valeur de retour d’une fonction qui va contenir les valeurs de retour d’une fonction. Souvenez-vous : c’est un peu comme un dossier !
Et ensuite pour manipuler votre tuple dans la console, rien de plus simple :
// Actuellement, Joe possède actuellement 112 bouteilles de lait dans sa ferme
print("Actuellement, Joe possède \(result.quantity) bouteilles de \(result.name) dans sa ferme")
Ou alors vous pouvez aussi le faire en appelant directement la fonction :
// Actuellement, Joe possède actuellement 112 bouteilles de lait dans sa ferme
print("Actuellement, Joe possède actuellement \(milkProduct().quantity) bouteilles de \(milkProduct().name) dans sa ferme")
Vous l’aurez compris, les tuples sont souvent utilisées pour retourner des valeurs multiples (et cela peu importe les types !) à partir d’un appel de fonction. Par exemple, le nom d’un produit, sa quantité ou encore sa disponibilité. Vous pouvez donc retourner soit des tuples anonymes soit des tuples nommés, comme bon vous semble !
L’avantage, c’est qu’on ne se contente pas de retourner une seule valeur d’un seul type, mais plusieurs valeurs de types différents en une fois.
À vous de jouer
Le contenu de l’exercice se trouve dans le dossier Github P4C1.
Ouvrez un nouveau Playground Xcode.
Copiez le contenu du fichier “main.swift” dans votre Playground.
Suivez les instructions.
Félicitations, vous maîtrisez les fonctions ! Votre code est quand même bien plus lisible comme ça, non ?
En résumé
Une fonction est une brique d'instructions que l'on regroupe derrière un nom.
On déclare une fonction avec le mot-clé
func
comme ceci :
func nomDeLaFonction() {
// Implémentation
}
Une fonction peut avoir une valeur de retour. Pour cela, il faut préciser son type dans la déclaration comme ceci :
func nomDeMaFonction() -> Type {
// Implementation
return valeur
}
Une variable n'existe que dans le contexte dans lequel (les accolades à l'intérieur desquelles) elle a été définie.
Les tuples permettent à une fonction de pouvoir retourner plusieurs valeurs, et de types différents (ou pas !).
Vous venez d’apprendre à créer vos premières fonctions, je suis vraiment fier de vous ! Néanmoins, la programmation ne s’arrête pas à la création de fonctions, il reste encore plein de concepts merveilleux à découvrir. Et si je vous disais que dans le prochain chapitre je vais vous montrer comment on entre dans la matrice, ça vous dit ?