Les fonctions sont des morceaux de code autonomes qui exécutent une tâche spécifique. Pour faire simple, ce sont des lignes de code qui sont séparées du reste de votre code par des accolades. Ces lignes de code ne seront interprétées que si vous appelez la fonction.

On donne à une fonction un nom qui identifie ce qu'elle fait, et ce nom est utilisé pour appeler la fonction.

Ce chapitre fait partie des plus longs de ce tutoriel sur Swift. Ne vous découragez pas car ce que vous allez découvrir est extra-ordinaire. Les fonctions sont sur-puissantes et omniprésentes en programmation. Let's go !

Déclarer une fonction

On déclare une fonction en commençant par le mot-clé func suivi du nom de la fonction et de parenthèses. On indique ensuite qu'il s'agit du corps de la fonction en ouvrant les accolades. C'est dans cette partie que les instructions seront listées les unes après les autres. Et enfin, on referme les accolades pour signifier la fin de la fonction.


func saluer() {
    print("Salut, comment ca va ?")
}

Dans l'exemple ci-dessus, on a déclaré une fonction saluer(). Cette fonction ne fait qu'une seule chose, elle affiche "Salut, comment ca va ?". Maintenant, à chaque fois que l'on croisera quelqu'un, on appellera la fonction saluer() pour lui dire bonjour. Pratique, non ?


saluer()
// Affichera : Salut, comment ca va ?

Une fonction peut contenir des paramètres. Ces paramètres sont indiqués juste après le nom de la fonction entre parenthèses. Pour reprendre notre fonction saluer(), nous allons pouvoir la personnaliser grâce à certains paramètres :


func saluer(nom: String) {
    print("Salut \(nom), comment ca va ?")
}

saluer(nom :"Jean")
// Affichera : Salut Jean, comment ca va ?

Lors de la déclaration de la fonction saluer(nom:), nous avons ajouté un paramètre à cette fonction. On l'écrit entre parenthèses juste après le nom de la fonction. Ce paramètre se nomme nom et il est de type String. Cela signifie que pour appeler la fonction, il faut lui passer un paramètre nommé nom et qu'il soit de type String ou cela provoquera une erreur.

Pour appeler cette fonction, il suffit de spécifier son nom suivi des arguments nécessaires entre parenthèses. Nous l'appelons en spécifiant son nom, saluer, suivi de l'argument nom: "Jean" entre parenthèses.

Il est important de s'assurer que les arguments fournis lors de l'appel de la fonction correspondent à ceux attendus par la fonction, en terme de type et de nombre. Sinon, une erreur peut se produire lors de l'exécution du code.

Paramètre d'une fonction

Comme nous venons de le voir, lorsque vous définissez une fonction, vous pouvez si vous le souhaitez définir une ou plusieurs valeurs nommées et typées que la fonction prend en entrée, appelées paramètres.

Fonction sans paramètre

Notre toute première fonction est une fonction sans paramètre et sans valeur de retour. Elle n'a pas besoin de paramètre pour fonctionner et elle ne renvoie aucune valeur, elle effectue juste les actions entre les accolades.


func saluer() {
    print("Salut, comment ca va ?")
}

La déclaration d'une fonction a toujours besoin de parenthèses après le nom de la fonction, même si elle ne prend aucun paramètre. Le nom de la fonction est également suivi d'une paire de parenthèses vides lorsque la fonction est appelée.

Fonction avec plusieurs paramètres


func saluer(nom: String, age: Int) {
    if age < 18 {
        print("Dehors \(nom)")
    } else {
        print("Bienvenue \(nom)")
    }
}

saluer(nom :"Justine", age: 22)
// Cela affichera : Bienvenue Justine

Les fonctions peuvent avoir plusieurs paramètres d'entrée, qui sont écrits entre les parenthèses de la fonction, séparés par des virgules. Dans l'exemple ci-dessus, la fonction saluer(nom:, age:) prend deux paramètres. L'un se nomme nom et est de type String et l'autre se nomme age et est de type Int.

Pour appeler cette fonction, on indique son nom suivi des deux paramètres entre parenthèses. Veuillez à ce que les paramètres soient dans l'ordre et du type attendu.

Valeur par défaut des paramètres

Vous pouvez définir une valeur par défaut pour n'importe quel paramètre d'une fonction en attribuant une valeur au paramètre après le type de ce paramètre. Si une valeur par défaut est définie, vous n'êtes pas obligé d'inclure ce paramètre dans l'appel de la fonction.


func saluer(nom: String, jour: Bool = true) {
    
    if jour {
        print("Bonjour \(nom)")
    } else {
        print("Bonsoir \(nom)")
    }
    
}

saluer(nom: "Jean")
// Cela affichera : Bonjour Jean

saluer(nom: "Jeanne", jour: false)
// Cela affichera : Bonsoir Jeanne

Lors du premier appel de la fonction saluer(nom:, jour:), on indique uniquement la valeur du premier paramètre qui se nomme nom. On n'est pas obligé d'écrire le second vu qu'il a une valeur par défaut.

Ce second paramètre nommé jour a pour valeur par défaut true. S'il n'est pas défini lors de l'appel de la fonction, le paramètre jour aura donc pour valeur celle par défaut soit true.

Au second appel de la fonction saluer(nom:, jour:), on définit les paramètres nom qui a pour valeur "Jeanne" et jour qui a pour valeur false.

La valeur par défaut du paramètre jour est true si aucune autre valeur ne lui est attribuée lors de l'appel de la fonction. Mais, lorsqu'on appelle cette fonction, on définit le paramètre jour en lui assignant comme valeur false donc la valeur true, valeur par défaut , sera remplacée par false.

Il est préférable de placer les paramètres qui n'ont pas de valeur par défaut au début de la liste des paramètres d'une fonction, avant les paramètres qui ont des valeurs par défaut. Les paramètres qui n'ont pas de valeur par défaut sont généralement plus importants pour la signification de la fonction - les écrire en premier permet de reconnaître plus facilement que la même fonction est appelée, que des paramètres par défaut soient omis ou non.

Paramètre variadique

En Swift, les paramètres variadiques permettent à une fonction de recevoir un nombre variable d'arguments d'un même type. Cela signifie que vous pouvez appeler une fonction avec un nombre variable d'arguments du type spécifié, et la fonction les recevra sous la forme d'un tableau.

Vous pouvez déclarer des paramètres variadiques à l'aide de l'opérateur de points de suspension (...) :


func maFonctionVariadique(nombres: Int...) {
    for nombre in nombres {
        print(nombre)
    }
}

maFonctionVariadique(nombres: 1, 2, 3)
// Cela affichera : 1 2 3

Dans cet exemple, la fonction maFonctionVariadique prend un nombre variable d'arguments de type Int. À l'intérieur de la fonction, les paramètres variadiques sont traités comme un tableau.

Un paramètre variadique accepte zéro ou plusieurs valeurs d'un type spécifié. Vous utilisez un paramètre variadique pour spécifier que le paramètre peut recevoir un nombre variable de valeurs d'entrées lorsque la fonction est appelée.

Notez que vous ne pouvez avoir qu'un seul paramètre variadique par fonction, et il doit être le dernier paramètre. Si vous avez d'autres paramètres après celui variadique, vous devrez les spécifier explicitement lors de l'appel de la fonction.

Valeur de retour

Jusqu'ici nos fonctions ne faisaient que suivre des instructions, on leur demandait juste d'afficher un texte. Imaginons une fonction qui multiplie un nombre par lui même. Cela donnerait comme résultat le nombre au carré. Mais, plutôt que de l'afficher, nous voudrions que la fonction nous renvoie cette valeur pour que nous puissions nous en servir.

Pour indiquer qu'une fonction renvoie une valeur, on le signale dès la déclaration de la fonction. Après les parenthèses des paramètres de la fonction, on indique le type de retour. Dans notre exemple, notre fonction renverra la valeur au carré d'un nombre, le retour sera donc de type Int :


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

let resultat = carre(nombre: 4)

print("Le carré de 4 est \(resultat)")
// Cela affichera : Le carré de 4 est 16

On utilise le trait d'union et le signe supérieur pour afficher une flèche qui symbolise le retour suivi du type de ce retour (->). La fonction renvoie le nombre multiplié par le nombre. Le retour de la fonction est donc de type Int. On l'indique juste après la flèche.

On définit, en option, un type de valeur que la fonction transmettra en sortie lorsqu'elle sera terminée : cela s'appelle le type de retour.

On utilise le mot-clé return pour indiquer la valeur à retourner. Lorsqu'on appelle cette fonction, on récupère la valeur envoyée grâce au mot-clé return. La valeur transmise en retour doit être du même type que celle indiquée lors de la déclaration du retour de la fonction sous peine de provoquer une erreur.

L'instruction return en Swift permet de retourner une valeur d'une fonction et d'arrêter son exécution. Une fois atteinte, le reste du code dans la fonction n'est plus exécuté et le contrôle est renvoyé au code appelant la fonction.

Retour de tuple

Vous pouvez utiliser un tuple comme type de retour pour une fonction afin de renvoyer plusieurs valeurs :


func minMax(valeur1: Int, valeur2: Int) -> (min: Int, max: Int) {
    var retour: (min: Int, max: Int) = (0, 0)
    if valeur1 > valeur2 {
        retour = (min: valeur2, max: valeur1)
    } else {
        retour = (min: valeur1, max: valeur2)
    }
    return retour
}

let nombre1 = 13
let nombre2 = 16

let nombreTrier = minMax(valeur1: nombre1, valeur2: nombre2)

print("Le nombre le plus grand est \(nombreTrier.max) et le nombre le plus petit est \(nombreTrier.min)")
// CEla affichera : Le nombre le plus grand est 16 et le nombre le plus petit est 13

La fonction minMax(valeur1:, valeur2:) prend deux paramètres en entrée de type Int. Elle renvoie, comme type de retour, un tuple de type (Int, Int). Le corps de cette fonction est contenu entre les accolades.

On commence par déclarer un tuple et on lui assigne une valeur par défaut. On teste ensuite à l'aide d'une condition if quelle valeur des deux paramètres d'entrée est la plus grande. Suivant le résultat de la condition, on assigne l'une des valeurs d'entrée à l'étiquette min et l'autre à l'étiquette max.

Enfin, on retourne un tuple contenant des valeurs de type Int avec les étiquettes : min et max. Cela nous permet d'accèder facilement aux valeurs de ce tuple pour les afficher.

Fonction avec retour implicite

Si le corps entier de la fonction est une seule expression, la fonction renvoie implicitement cette expression. Toute fonction que vous écrivez sur une seule ligne peut omettre le return.


func saluer(nom: String) -> String {
    "Bonjour, " + nom + "!"
}

Étiquette d'argument et paramètre

En Swift, comme dans de nombreux langages de programmation, une fonction peut avoir des paramètres. Les paramètres sont des valeurs que vous fournissez à une fonction lorsque vous l'appelez, et ils peuvent être utilisés à l'intérieur de la fonction pour effectuer des opérations.

Une étiquette d'argument (ou "label" en anglais) est une manière de nommer un paramètre lors de l'appel d'une fonction. Elle est utilisée pour améliorer la lisibilité du code et rendre plus clair le rôle de chaque argument dans le contexte de l'appel de la fonction.


func saluer(nom: String, de ville: String) -> String {
    "Bonjour, " + nom + " qui nous vient de " + ville + " !"
}

print(saluer(nom: "Jean", de: "Paris"))
// Cela affichera : Bonjour, Jean qui nous vient de Paris !

L'étiquette d'argument est utilisée lors de l'appel de la fonction . Chaque paramètre est écrit avec son étiquette d'argument devant lui.

Le nom du paramètre est utilisé dans l'implémentation de la fonction . Par défaut, les paramètres utilisent leur nom de paramètre comme étiquette d'argument.

Omettre les étiquettes d'arguments

Si vous ne voulez pas d'étiquette d'argument pour un paramètre, écrivez un trait de soulignement _ (underscore) au lieu d'une étiquette d'argument explicite pour ce paramètre. Cela permet, lors de l'appel d'une fonction, d'indiquer uniquement la valeur à transmettre sans écrire le nom du paramètre ou de son étiquette :


func saluer(_ nom: String, de ville: String) -> String {
    "Bonjour, " + nom + " qui nous vient de " + ville + " !"
}

print(saluer("Michel", de: "Toulouse"))
// Cela affichera : Bonjour, Michel qui nous vient de Toulouse !

Si un paramètre a une étiquette d'argument, l'argument doit être étiqueté lorsque vous appelez la fonction. Pour transmettre la valeur assignée au paramètre nommé ville et qui a pour étiquette de, il est obligatoire d'utiliser le nom de son étiquette (de) et non pas le nom de son paramètre (ville). Sinon cela provoquerait une erreur.

L'utilisation d'étiquettes d'arguments peut permettre à une fonction d'être appelée d'une manière expressive, semblable à une phrase, tout en fournissant un corps de fonction lisible et clair dans l'intention.

Dans certaines situations, l'omission de l'étiquette d'argument d'un paramètre s'avère bien plus pratique. On y gagne en rapidité lors de l'appel de la fonction et en lisibilité.

Sans omission d'étiquette :


// 
func carre(nombre: Int) -> Int {
    nombre * nombre
}

carre(nombre: 4)

Avec omission d'étiquette :


func carre(_ nombre: Int) -> Int {
    nombre * nombre
}

carre(4)

Types de fonctions

Chaque fonction en Swift a un type. Il est composé des types de paramètres de la fonction et du type de retour. Vous pouvez utiliser ce type comme n'importe quel autre type dans Swift, ce qui facilite le passage de fonctions en tant que paramètres à d'autres fonctions et le renvoi de fonctions à partir de fonctions. Les fonctions peuvent également être écrites dans d'autres fonctions pour encapsuler des fonctionnalités.

Ça fait beaucoup d'un coup, ça peut paraitre compliqué, mais en fait, c'est très logique. Découvrons cela étape par étape.

Il est important de comprendre la syntaxe des fonctions en Swift pour pouvoir les utiliser correctement dans votre code. Reprenons l'exemple de notre fonction saluer(nom:) :


func saluer(nom: String) -> String {
  return "Bonjour, \(nom)!"
}

Dans cet exemple, nous définissons une fonction saluer(nom:) qui prend un argument en entrée nommé nom de type String et renvoie une valeur de type String.

Le type de cette fonction est : (String) -> String, qui se traduit par : "Une fonction avec un paramètre de type String et qui renvoie une valeur de type String"

Nous avons également vu que les fonctions n'ont pas nécessairement à définir un type de retour. Voici un exemple avec une autre version de la fonction saluer(nom:), qui "affiche" sa propre valeur String plutôt que de la renvoyer :


func saluer(nom: String) {
  print("Bonjour, \(nom)!")
}

Comme on ne revoie pas de valeur, la définition de la fonction n'inclut pas la flèche de retour ( ->) ou un type de retour.

Pour entrer dans les détails, la fonction saluer(nom:) renvoie toujours une valeur, même si aucune valeur de retour n'est définie. Les fonctions sans type de retour défini renvoient une valeur spéciale de type Void. Il s'agit simplement d'un tuple vide, qui s'écrit ().

Le type de cette fonction est donc ( ) -> Void, ou "une fonction qui n'a pas de paramètre et qui renvoie Void."

On utilise les types de fonctions comme tous les autres types dans Swift. Par exemple, on peut définir une constante ou une variable comme étant un type de fonction et affecter une fonction appropriée à cette variable :


func saluer(nom: String) -> String {
  return "Bonjour, \(nom)!"
}

var bonjourJean: (String) -> String = saluer

La fonction saluer(nom:) prend un paramètre de type String en entrée, et lorsqu'on assigne une fonction à une variable, on ne fournit pas les arguments de la fonction.

On définit une variable appelée bonjourJean, qui a un type de fonction qui prend une valeur de type String et renvoie une valeur String. On peut maintenant appeler la fonction attribuée avec le nom bonjourJean :


print(bonjourJean("Jean")) 
// Cela affichera "Bonjour, Jean!"

Et comme pour tout autre type, vous pouvez laisser à Swift le soin de déduire le type de fonction grâce à l'interférence de type :


let bonjourMichel = saluer
// bonjourMichel est supposé être de type (String) -> String

Types de fonction en tant que types de paramètres

On peut utiliser un type de fonction comme un type de paramètre pour une autre fonction :


// Fonction qui prend deux entiers et renvoie leur somme
func additionner(_ a: Int, _ b: Int) -> Int {
    return a + b
}

// Fonction qui prend deux entiers et renvoie leur produit
func multiplier(_ a: Int, _ b: Int) -> Int {
    return a * b
}

// Fonction générique qui prend deux entiers et une fonction en paramètre
// Elle applique la fonction sur les deux entiers et renvoie le résultat
func effectuerOperation(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
}

// Utilisation de la fonction avec l'opération d'addition
let resultatAddition = effectuerOperation(5, 3, operation: additionner)
print("Résultat de l'addition : \(resultatAddition)")  
// Affiche "Résultat de l'addition : 8"

// Utilisation de la fonction avec l'opération de multiplication
let resultatMultiplication = effectuerOperation(5, 3, operation: multiplier)
print("Résultat de la multiplication : \(resultatMultiplication)")  
// Affiche "Résultat de la multiplication : 15"

Dans l'exemple ci-dessus, on déclare une fonction additionner(_:, _:) qui a deux paramètres de type Int en entrée et renvoie une valeur de type Int. Cette fonction sert à additionner les deux nombres qu'on lui transmet et retourne leur somme.

La seconde fonction multiplier(_:, _:) a deux paramètres de type Int en entrée et renvoie une valeur de type Int. Cette fonction sert à multiplier les deux nombres qu'on lui transmet et retourne leur produit.

La troisième fonction effectuerOperation(_:, _:, operation:) est légèrement plus complexe. Elle accepte trois paramètres en entrée : les deux premiers sont de type Int et le troisième est de type (Int, Int) -> Int. Le troisième paramètre de cette fonction sera une autre fonction. Elle sert à effectuer une opération sur les deux nombres qu'on lui transmet et renvoie le résultat de cette opération.

Enfin, nous appelons cette dernière fonction en l'assignant à des constantes nommées resultatAddition et resultatMultiplication. Lorsqu'on appelle la fonction effectuerOperation via la constante resultatAddition, nous lui transmettons trois valeurs : 3, 5 et la fonction additionner. Et, lorsque nous appelons cette même fonction effectuerOperation via la constante resultatMultiplication, nous lui transmettons trois valeurs également : 3, 5 et la fonction multiplier.

Libre à nous de créer d'autres fonctions qui pourraient servir de paramètre pour la fonction effectuerOperation. Notre seule restriction sera de concevoir des fonctions de type (Int, Int) -> Int pour qu'elles puissent être des paramètres valides.

C'est un principe qui peut paraitre un peu complexe pour un néophyte en programmation. Rassurez-vous si c'est un peu obscur dans votre tête. Déjà, vous n'avez pas besoin de tout retenir par coeur. Par contre, il est important de comprendre ce concept, alors prenez le temps nécessaire pour bien saisir toutes les nuances du code ci-dessus.

Types de fonction comme types de retour

On peut utiliser un type de fonction comme type de retour d'une autre fonction. Pour cela, on écrit un type de fonction complet immédiatement après la flèche de retour ( ->) de la fonction renvoyée.

L'exemple suivant définit deux fonctions simples appelées incrementation(_:) et decrementation(_:). La fonction incrementation(_:) renvoie une valeur de un de plus que sa valeur d'entrée et la fonction decrementation(_:) renvoie une valeur de un de moins que sa valeur d'entrée. Les deux fonctions sont de type de (Int) -> Int.


// Incrémentation : +1
func incrementation(_ nombre: Int) -> Int {
    return nombre + 1
}

// Décrémentation : -1
func decrementation(_ nombre: Int) -> Int {
    return nombre - 1
}

La fonction suivante nommée choisir(arriere:) est de type (Int) -> Int. Cette fonction renvoie la fonction incrementation ou la fonction decrementation en fonction du paramètre booléen appelé arriere.


func choisir(arriere: Bool) -> (Int) -> Int {
    return arriere ? decrementation : incrementation
}

Vous pouvez maintenant utiliser la fonction choisir(arriere:) pour obtenir une fonction qui va avancer dans un sens ou dans l'autre :


var valeurActuelle = 3
let compterJusquaZero = choisir(arriere: valeurActuelle > 0)
// compterJusquaZero fait maintenant référence à la fonction decrementation()

L'exemple ci-dessus détermine si une étape positive ou négative est nécessaire pour que la variable valeurActuelle se rapproche progressivement de zéro. valeurActuelle a une valeur initiale de 3 donc le paramètre nommé arriere a pour valeur true donc la fonction choisir retournera la fonction decrementation.

Voyons cela dans un exemple plus concret, un compte à rebours :


// Incrémentation : +1
func incrementation(_ input: Int) -> Int {
    return input + 1
}

// Décrémentation : -1
func decrementation(_ input: Int) -> Int {
    return input - 1
}

func choisir(arriere: Bool) -> (Int) -> Int {
    return arriere ? decrementation : incrementation
}

var valeurActuelle = 3
let compterJusquaZero = choisir(arriere: valeurActuelle > 0)


print("Compte à rebours :")

// Compte à rebours :
while valeurActuelle != 0 {
    print("\(valeurActuelle)... ")
    valeurActuelle = compterJusquaZero(valeurActuelle)
}

print("zero!")

// 3...
// 2...
// 1...
// zero!

On utilise une boucle while pour passer en revue les chiffres restant en allant jusqu'à zéro. La condition de cette boucle est que la valeur de la variable valeurActuelle soit différente de zéro. Cela signifierait que l'objectif est atteint. Initialement, la variable valeurActuelle a pour valeur 3.

Donc, la valeur de la variable valeurActuelle étant différente de zéro, on entre dans une première boucle. On affiche le nombre actuel contenu dans la variable valeurActuelle et on appelle la fonction compterJusquaZero en lui transmettant comme paramètre la valeur de la variable valeurActuelle.

On appelle cette fonction directement depuis la variable valeurActuelle pour que la valeur qu'elle stocke soit mise à jour.

La fonction compterJusquaZero appelle à son tour la fonction choisir en lui transmettant comme paramètre la valeur de la variable valeurActuelle qui sera testée si oui ou non elle est supérieure à 0. Si c'est le cas, la fonction decrementation sera appelée et renverra la valeur de la variable valeurActuelle moins 1. Sinon, la fonction incrementation sera appelée et renverra la valeur de la variable valeurActuelle plus 1.

Une fois encore prenez le temps de bien comprendre ce principe. Décortiquez chaque étape. Dans le langage Swift, deux ou trois points sont un peu plus compliqués que les autres. Les fonctions en font partie.

Portée et fonction imbriquée ou nested

Toutes les fonctions que vous avez rencontré précédemment sont des fonctions globales, qui ont une portée globale. Vous pouvez également définir des fonctions à l'intérieur du corps d'autres fonctions, appelées fonctions imbriquées ou nested .

Lorsqu'on déclare une variable, une constante ou une fonction à l'intérieur d'une fonction, celles-ci ne sont accessibles que depuis le corps de la fonction. Entre les accolades. Si vous cherchez à utiliser cette variable, cette constante ou cette fonction ailleurs que dans le corps de la fonction, cela provoquera une erreur de portée.

La portée des variables et des fonctions est définie par l'endroit où elles sont déclarées :

  • Portée globale : Les variables, les constantes et les fonctions déclarées en dehors de toutes les fonctions ou blocs de code type boucle ont une portée globale. Elles peuvent être utilisées partout dans le fichier source où elles sont déclarées. La portée globale est souvent utilisée pour les constantes, variables ou fonctions qui doivent être accessibles depuis n'importe quel endroit du fichier.
  • Portée locale : Les variables, les constantes et les fonctions déclarées à l'intérieur d'une fonction sont accessibles uniquement à l'intérieur de cette fonction. Elles sont appelées variables locales et ne sont pas visibles et utilisables en dehors de la fonction.

Les fonctions imbriquées sont cachées du monde extérieur par défaut, mais peuvent toujours être appelées et utilisées par leur fonction englobante. Dans l'exemple précédent, les fonctions incrementation et decrementation ont une portée globale. Elles peuvent donc être utilisées depuis n'importe ou dans votre code.

En imbriquant ces deux fonctions dans la fonction choisir, on les rend inaccessibles et inutilisables. Elles ont désormais une portée locale. Cela peut éviter certaines confusions ou problèmes. :


func choisir(arriere: Bool) -> (Int) -> Int {
    func incrementation(nombre: Int) -> Int { return nombre + 1 }
    func decrementation(nombre: Int) -> Int { return nombre - 1 }
    return arriere ? decrementation : incrementation
}

var valeurActuelle = -4
let compterJusquaZero = choisir(arriere: valeurActuelle > 0)


while valeurActuelle != 0 {
    print("\(valeurActuelle)... ")
    valeurActuelle = compterJusquaZero(valeurActuelle)
}

print("zero!")
// -4..., -3..., -2..., -1..., zero!

Le principe est exactement le même que pour l'écriture précédente de ce code. Les seules différences sont que les deux fonctions incrementation et decrementation sont imbriquées à la fonction choisir et que la valeur de la variable valeurActuelle a été initialisée sur -4.

La valeur de la variable valeurActuelle étant négative, la fonction choisir retournera donc la fonction incrementation cette fois. Sinon, le reste du code fonctionne exactement de la même manière que pour l'exemple précédent.

Condition guard

Le dernier point que nous allons voir pour ce chapitre concerne la condition guard. C'est une structure de contrôle qui permet de définir des conditions pour la sortie anticipée d'un bloc de code. La condition guard a quelques similitudes avec la condition if.

L'instruction guard est utilisée pour garantir qu'une condition particulière est vraie, sinon elle exécute un bloc de code spécifié. Elle est souvent utilisée pour effectuer des vérifications de conditions et sortir de la fonction, de la boucle, ou de la portée actuelle si la condition n'est pas satisfaite.


guard condition else {
    // Code à exécuter si la condition n'est pas vraie
    // Sortie anticipée de la portée courante
}
// Code à exécuter si la condition est vraie
// La portée continue ici

Une instruction guard est utilisée pour transférer le contrôle du programme hors d'une portée si une ou plusieurs conditions ne sont pas remplies. La clause Else est obligatoire lors d'une condition guard.

Voici un exemple concret pour illustrer son utilisation dans une fonction :


func diviser(_ numerateur: Int, par denominateur: Int) -> Int {
    // Utilisation de guard pour vérifier si le dénominateur est différent de zéro
    guard denominateur != 0 else {
        // Sortie anticipée si le dénominateur est zéro
        print("Erreur : Division par zéro n'est pas autorisée.")
        return 0
    }
    
    // La division est effectuée uniquement si le dénominateur est différent de zéro
    let quotient = numerateur / denominateur
    return quotient
}

print(diviser(8, par: 2))
// Cela affichera : 4

Dans cet exemple, la condition guard vérifie si la valeur passée en paramètre nommé denominateur a pour valeur 0. Si c'est le cas, le bloc de code à l'intérieur des accolades est exécuté, affichant un message et sortant de la fonction à l'aide du mot-clé return . Si la valeur est différente de zéro, le code après le bloc guard est exécuté normalement.

Il est également possible d'utiliser la condition guard avec des structures de contrôle plus complexes pour vérifier plusieurs conditions :


func grade(score: Int) {
    guard score >= 90 else {
        print("Désolé, vous avez échoué.")
        return
    }
    guard score > 100 else {
        print("Score impossible.")
        return
    }
    print("Félicitations, vous avez réussi avec un score de \(score).")
}

Dans cet exemple, si le score est inférieur à 90, le message "Désolé, vous avez échoué." sera affiché et la fonction quittera. Si le score est supérieur à 100, le message "Score impossible." sera affiché et la fonction quittera aussi.

Ça y est, nous y sommes, le chapitre est terminé. Enfin presque. Prenez quelques minutes à lire les points essentiels dans le résumé du chapitre ci-dessous et effectuez l'exercice. La pratique est très importante en programmation car plus vous pratiquerez, plus vous comprendrez. Une page se tourne avec ce très gros morceau consacré aux fonctions, place aux closures !

  • On utilise le mot-clé func suivi du nom de la fonction pour déclarer une fonction.
  • On spécifie le type de retour d'une fonction en utilisant la flèche -> suivi du type de retour.
  • Utilisez le mot-clé return pour renvoyer une valeur depuis une fonction.
  • Les paramètres sont des valeurs transmises à une fonction lors de son appel.
  • Les variables déclarées à l'intérieur d'une fonction ont une portée locale, ce qui signifie qu'elles ne sont accessibles qu'à l'intérieur de cette fonction.
  • Si une fonction ne retourne aucune valeur, utilisez Void comme type de retour, ou ne spécifiez aucun type de retour.

Je vous conseille de faire chaque exercice de fin de chapitre. Cela consolidera votre apprentissage de manière efficace en passant de la théorie à la pratique. Faites vos propres experiences, n'ayez peur de rien, vous progresserez plus vite ainsi.

Dans cet exercice, vous allez créer une fonction qui vérifie si un mot est un palindrome. Un palindrome est un mot qui se lit de la même manière à l'endroit et à l'envers (par exemple, "radar" ou "level"). Je vous donne un petit indice, il se pourrait que vous ayez besoin de la méthode reversed()... Au boulot !

Plusieurs solutions sont possibles. En voici une qui fonctionne :


func isPalindrome(_ mot: String) -> String {
    
    // On stocke le mot dans le sens inverse
    let motInverse = String(mot.reversed())
    
    // On utilise l'opérateur conditionnel ternaire
    let resultat = (mot == motInverse ? "\"\(mot)\" est un palindrome !" : "\"\(mot)\" est un mot quelconque")
    
    return resultat
}

// Liste des mots à tester
let mots: [String] = ["bateau", "kayak", "barque", "chalut"]

for mot in mots {
    print(isPalindrome(mot))
}

// "bateau" est un mot quelconque
// "kayak" est un palindrome !
// "barque" est un mot quelconque
// "chalut" est un mot quelconque