Mis à jour le lundi 30 octobre 2017
  • 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Les fonctions et les closures

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

‌Les fonctions sont très utiles pour éviter d'avoir un code redondant. Il vous est déjà arrivé, ou alors ça va arriver, d'écrire plusieurs fois la même chose pour obtenir le même type de résultat. Du coup, vous avez l'impression qu'écrire du code se résume à copier-coller plusieurs morceaux de son propre code.

Les fonctions sont présentes pour exécuter certaines tâches spécifiques. Et cela autant de fois que vous le souhaitez, sans avoir à toujours écrire la même chose. Elles vont permettre de simplifier la modification de votre code mais aussi de donner un aspect plus clair lors de la lecture.

Qu'est-ce qu'une fonction ?

Une fonction est une portion de code qui vous permet d'exécuter une suite d'instructions et qui va vous retourner ou non une valeur. L'exécution pourra se faire grâce à des paramètres que l'on aura précisés. 

Bien sûr, il ne faut pas voir des fonctions partout. En général, on crée des fonctions dès lors que l'on a besoin d'exécuter des opérations assez complexes et surtout, que l'on envisage de répéter ces opérations plus tard dans le programme. 

En Swift, il existe énormément de fonctions déjà prêtes à l'emploi. Vous n'avez plus qu'à les utiliser comme vous le souhaitez. Je pourrais vous les présenter tout de suite, mais cela nécessiterait une autre notion, plus complexe à apprendre, pour pouvoir les utiliser. Je ne voudrais pas vous embêter avec ces termes ; promis, je reviens là-dessus plus tard !

Exemple de fonction à créer

Prenons un tableau, sauriez-vous calculer le nombre de valeurs qu'il comporte ? Bien sûr que oui ! Comment ça, non ? Si vous ne vous en sentez pas capable, je vous recommande vivement de lire ces derniers chapitres. ;)

On parcourt le tableau à l'aide d'unfor-in, et on incrémente une variable. Une fois la boucle terminée, cette variable nous donnera le nombre de valeurs du tableau. Jusque là, c'est tout simple.

J'aime bien voir les choses en grand, alors voyons-les en grand. Vous avez maintenant 10 tableaux, susceptibles de varier au cours du programme. Vous ne savez donc pas exactement le nombre de valeurs que vos tableaux contiennent. Arrivé à un moment, vous souhaitez retourner le nombre de valeurs de ces tableaux, que faites-vous ? Ce que j'ai expliqué dans le paragraphe ci-avant. Seulement, vous ne le faites pas ici une fois, mais dix fois ! Ah et puis mince, je me suis trompé j'ai mis l'incrémentateur à 1 et il fallait le mettre à 0. Bon bah... je dois aller modifier à 10 endroits dans mon code cette valeur...

Le code devient vite gros et lourd. Ce n'est pas le but pour un développeur. Non, ce qu'on souhaiterait là c'est de pouvoir dire un truc comme ceci : "Pour calculer la longueur d'un tableau tu utilises cette méthode. Tu exécutes cette même méthode pour mon premier tableau et tu me dis sa valeur, tu re-fais la même chose mais cette fois-ci avec le deuxième tableau mais toujours avec la méthode que je t'ai fournie, idem pour le troisième tableau, etc., etc.". 

Vous avez déjà utilisé une fonction !

Je vous en avais parlé, mais trèèèèès vaguement. Je vous avez surtout promis de consacrer un chapitre sur ça, c'est chose faite et je tiens mes promesses. Vous vous rappelez ? print() !

Cette fonction est présente de base lors de la rédaction d'un programme. Et cette fonction va appeler tout une suite d'instructions (que vous ne voyez pas) et qui permet d'afficher le texte désiré dans votre console. Elle utilise donc un seul paramètre : le texte à afficher. Ensuite, appelez cette fonction et elle se chargera elle-même d'exécuter les instructions pour afficher votre texte dans la console.

Créer et utiliser ses propres fonctions

Une fonction peut se créer  différemment selon ce que l'on souhaite.

Fonctions sans paramètres

Voici la syntaxe pour définir une fonction :

func nomDeLaFonction() {
    // Instructions
}
  • On écrit le mot-cléfunc (diminutif de function, fonction en français).

  • S'ensuit le nom de votre fonction. Le nom devra respecter les mêmes conditions que pour le nom d'une variable et ne doit pas avoir le même nom qu'une variable existante. On termine par une paire de parenthèses : ouvrante et fermante.

  • On ouvre des accolades.

  • À l'intérieur de ces accolades, on exécute les instructions souhaitées pour cette fonction.

Exemple d'une fonction et de comment l'utiliser :

// On déclare la fonction
func disBonjour() {
    print("Bonjour !")
}

// Enfin, on peut s'en servir !
disBonjour()

Bon, pas vraiment d'intérêt, mais c'est histoire de vous montrer le fonctionnement d'une fonction tout doucement. 

Il est nécessaire d'écrire votre fonction dans le code avant l'endroit où vous souhaitez l'utiliser.

Remarquez dans cet exemple, on peut utiliser des fonctions dans nos fonctions. Et celà, sans limites !

// On déclare les fonctions
func disBonjour() {
    print("Bonjour !")
}

func disBonjourCommentCaVa() {
    disBonjour()
    print("Comment ça va ?")
}

// Enfin, on peut s'en servir !
disBonjourCommentCaVa()

Vous pouvez exécuter des fonctions dans lesquelles vous pouvez a priori faire tout ce que vous avez déjà appris. Vous pouvez déclarer des variables, utiliser des conditions, utiliser les boucles, bref tout ce que vous souhaitez uniquement pour cette fonction.

Fonctions avec paramètres

On peut aussi fournir des paramètres à notre fonction. Ces paramètres seront à préciser entre parenthèses. Vous pouvez en déclarer autant que vous le souhaitez. Dire bonjour c'est bien, mais dire bonjour à quelqu'un c'est encore mieux. Voici la syntaxe d'une fonction prenant des paramètres :

func disBonjour(parametre1: Type, parametre2: Type, ...) {
    // Instructions
}

La nouveauté ici est donc entre les parenthèses. On va préciser les paramètres à passer à la fonction. Ces paramètres seront des variables avec leur types explicites, séparés par des virgules. Pour dire bonjour à quelqu'un, je vais fournir à ma fonction son prénom. Je vais donc passer une chaîne de caractères. La fonction s'attendra à obtenir un paramètre de typeString . Voici comment l'écrire :

func disBonjour(prenom: String) {
    print("Bonjour " + prenom + " !")
}

// Pour l'utiliser
disBonjour(prenom: "Rudy")
disBonjour(prenom: "Théo")
disBonjour(prenom: "Flore")

On arrive à quelque chose de bien plus intéressant maintenant non ? La fonction s'attend à recevoir lors de son appel un élément de typeString. Ce qui a été passé en paramètre lors de l'utilisation de la fonction est récupéré dans la variableprenom de la déclaration de la fonction. Il ne reste plus qu'à se servir de cette variable. Pour utiliser la fonction, on précise entre parenthèses les paramètres à lui fournir. Exemple avec deux paramètres :

func disBonjour(prenom: String, nom: String) {
    print("Bonjour " + prenom + " " + nom + " !")
}

// Pour l'utiliser
disBonjour(prenom:"Jean", nom: "Dupont")
disBonjour(prenom:"Robert", nom: "Durand")

J'ai dit dans les chapitres précédents, que Swift devait être facile à comprendre rien que par une lecture de code. Ici, si on lit  disBonjour(prenom: "Rudy") dans notre tête on lira "Dis bonjour prenom Rudy". Vous comprenez bien que cette phrase dans un français correct n'a aucun sens ! Non, ce qu'on voudrait nous de ce fait c'est plutôt qu'on se dise dans la tête "Dis bonjour à Rudy". Là c'est bien mieux ! On écrirait alors ce code :

func disBonjour(a: String) {
    print("Bonjour " + a + " !")
}

disBonjour(a: "Rudy")

Le disBonjour(a: "Rudy") passe bien mieux ici. Nouveau soucis maintenant, j'espère que ça vous interpelle !

print("Bonjour " + a + " !")

"Bonjour a" ?! Non, moi je veux garder "Bonjour prenom", ça sonne mieux dans ma tête !

Et bien, sachez que Apple a prévu ce cas de figure et voici comment l'on pourrait résoudre nos soucis.

func disBonjour(a prenom: String) {
    print("Bonjour " + prenom + " !")
}

disBonjour(a: "Rudy")

Avec cette méthode, le premier mot employé dans le premier paramètre sera celui utilisé lorsqu'on appelera la fonction. Le deuxième mot sera celui utilisé dans le corps de la fonction.

Vous pouvez jouer de cette façon avec les paramètres autant que vous le voulez sur les autres paramètres :

func disBonjour(a prenom: String, et prenom2: String) {
    print("Bonjour " + prenom + " et " + prenom2 + "!")
}

disBonjour(a: "Rudy", et: "Théo")

On peut aussi omettre le nom du paramètre lorsqu'on appelle la fonction. Pour cela, remplacez le premier mot par un underscore "_".

func disBonjour(_ prenom: String, et prenom2: String) {
    print("Bonjour " + prenom + " et " + prenom2 + "!")
}

disBonjour("Rudy", et: "Théo")

Fonctions avec un retour

On pourrait s'imaginer maintenant une fonction qui calcule le carré d'un nombre ou encore qui calcule le périmètre d'un rectangle. Seulement, cette fois-ci on souhaiterait récupérer la valeur pour s'en servir ensuite. On va donc préciser à notre fonction de lui exécuter certaines actions, et de nous fournir la valeur que l'on souhaite. Voici la syntaxe d'une fonction qui retourne une valeur.

func nomDeLaFonction(parametre1: Type, parametre2: Type, ...) -> TypeRetour {
    // Instructions
    
    return laValeurARetourner
}

La nouveauté cette fois-ci c'est-> TypeRetour . Le carré d'un nombre entier sera forcément un nombre entier. Le périmètre d'un rectangle avec pour longueur et largeur un nombre à virgule sera forcément un nombre à virgule. Il faut le préciser à notre fonction.

Une autre nouveauté, c'est : return laValeurARetourner. Cela permettra de dire ce que notre fonction doit retourner. Dès l'instant que le programme arrive sur lereturn, la fonction se termine et on en sort, même s'il restait des instructions à exécuter.

Pour récupérer la valeur, il suffira de la mettre dans une variable.

func calculeCarre(de nombre: Int) -> Int {
    let nombre = nombre * nombre
    
    return nombre
}

func calculePerimetreRectangle(longueur: Double, largeur: Double) -> Double {
    let perimetre = (longueur + largeur) * 2
    
    return perimetre
}

// Pour récupérer ces valeurs
let carreDe5 = calculeCarre(de: 5)
print("Le carré de 5 est \(carreDe5).")

let largeur = 2.5
let longueur = 5.0
let perimetre = calculePerimetreRectangle(longueur: longueur, largeur: largeur)
print("Le périmètre du rectangle est de \(perimetre) cm.")

Faites bien attention que le type de la valeur que vous retournez est bien celui que vous avez précisé comme type de retour. Autrement, Xcode vous le dira de toute façon, car une erreur fera son apparition.

On aurait pu écrire ces fonctions ci-dessus d'une manière plus rapide :

func calculeCarre(de nombre: Int) -> Int {
    return nombre * nombre
}

func calculePerimetreRectangle(longueur: Double, largeur: Double) -> Double {
    return (longueur + largeur) * 2
}

Créez autant de fonctions que nécessaire. Vous n'avez pas de limites de création, donc faites-en bon usage !

Je n'ai ici utilisé qu'un seul mot comme nom de paramètre de la deuxième fonction. J'ai tout à fait le droit, je trouvais juste que cela été assez clair pour moi. ;) 

Une fonction comme paramètre ou type de retour

Il est possible de passer en paramètre une fonction. Soit on donne directement le nom d'une fonction déjà définie, soit on utilise les closures que nous verrons juste après. Il faut juste préciser dans la définition de la fonction à quel genre de fonction on s'attend en paramètre : nombre de paramètres de la fonction avec leurs types et son type de retour. Dans la définition, on ne nomme pas la fonction, on précise seulement sa syntaxe. Par exemple :

func maFonction(maFonctionParametre: (Int, Int) -> Bool) {
    // On peut utiliser la fonction passé en paramètre comme ceci
    maFonctionParametre(12, 123)
}

La fonctionmaFonction  s'attend ici à recevoir en paramètre une fonction qui prend un premier paramètre de type Int  et un deuxième paramètre de typeInt  et qui retourne unBool . On pourrait donc avoir ce genre de code :

func premierPlusGrandQueDeuxieme(nb1: Int, nb2: Int) -> Bool {
    return nb1 > nb2 // nb1 > nb2 retourne un booléen si c'est vrai ou non
}

func maFonction(maFonctionParametre: (Int, Int) -> Bool) {
    if maFonctionParametre(4, 3) {
        print("Condition validée.")
    }
}

// On a juste à fournir le nom de notre fonction
// A condition qu'elle respecte les paramètres et type de retour
maFonction(maFonctionParametre: premierPlusGrandQueDeuxieme)

// Affichera "Condition validée"

De la même façon, vous pouvez retourner une fonction en le précisant dans le type de retour.

func maFonction(param1: Int, param2: Bool) -> (Int, Int) -> Bool {
    // ...
}

Ici, cette fonction nous retournera une fonction qui prend deux paramètres de typeInt  et qui retourne unBool . Voici un exemple :

func hello(debutMessage: String) -> (String) -> String {
    
    func nestedHello(finMessage: String) -> String {
        return "\(debutMessage) \(finMessage)"
    }
    return nestedHello
}

print(hello(debutMessage: "Hello, ")("World !"))
// Affichera "Hello World !"

J'en profite avec cet exemple pour indiquer qu'on peut tout à fait déclarer des fonctions dans des fonctions. On appelle cela des Nested Functions (fonctions nichées en français). Ces fonctions ne seront fonctionnelles que à l'intérieur de la fonction et pas ailleurs.

Les closures

Les closures sont des fonctions un peu particulière : elle ne possède pas de nom. Elles sont utilisées pour les derniers cas que je vous ai présentés ci-dessus pour les fonctions. Les closures vont se définir directement dans l'appel d'une fonction.

Voici la syntaxe d'une closure :

{ (parametres) -> TypeRetour in
    // Instructions
}

On pourrait reprendre un exemple présenté au dessus pour intégrer une closure :

func premierPlusGrandQueDeuxieme(nb1: Int, nb2: Int) -> Bool {
    return nb1 > nb2
}

func maFonction(maFonctionParametre: (Int, Int) -> Bool) {
    if maFonctionParametre(4, 3) {
        print("Condition validée.")
    }
}

maFonction(maFonctionParametre: premierPlusGrandQueDeuxieme)

// Affichera "Condition validée"

Et cet exemple pourrait se réduire à celui-ci :

func maFonction(maFonctionParametre: (Int, Int) -> Bool) {
    if maFonctionParametre(4, 3) {
        print("Condition validée.")
    }
}

maFonction(maFonctionParametre: { (nb1: Int, nb2: Int) -> Bool in
    return nb1 > nb2
})

Notre code se voit ainsi simplifié et c'est là que réside le but d'une closure expression. :)

Ce qui donne:

maFonction { (nb1: Int, nb2: Int) -> Bool in
    return nb1 > nb2
}

Pour résumer

  • Une fonction est une portion de code qui va permettre d'exécuter des instructions selon des paramètres fournis ou non.

  • On déclare une fonction grâce à son mot-clé :func.

  • Une fonction peut ne pas retourner de valeur, mais peut aussi en retourner : on précise le type grâce à->  et on donne la valeur à retourner entre accolades grâce au mot-cléreturn.

  • On peut définir une fonction directement lors de l'appel grâce aux closures expressions.

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