• 30 hours
  • Easy

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/26/20

Allez plus loin avec les fonctions

Log in or subscribe for free to enjoy all this course has to offer!

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 ne s'arrête pas de fixer la barre plus haut ! Et oui c'est Joe, notre geek converti ! Voilà ce qu'il vous propose :

from: joe@lafermedejoe.fr

subject : Wow !

Vraiment je suis épaté parce 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 ceux-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

 Les fonctions avec paramètres !

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 en modifier son comportement.

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éeajouteDeux , créée avec le mot-clé func et qui retourne une valeur de typeInt. Si ce n'est pas très clair, pas de souci, rafraichissez-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, icinombre et un type, ici Int, séparé 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é !

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 ,.

14906931642575_Exercice%20Banner.png

Bon allez, à vous de jouer ! Je vous propose le petit exercice suivant. Écrivez une fonction appelée canWeSit (pouvons-nous nous asseoir) qui prend deux paramètres : le nombre de places assises disponibles dans un bus et le nombre de personnes dans le bus et qui imprime dans la console :

  • "Il y a encore de la place.", s’il reste des places assises.

  • "Il n'y a plus de place...", s’il n'y a plus de places assisses.

  • "Il y a des gens debout !", s’il y a trop de personnes dans le bus.

 

// A vous de jouer !
















































func canWeSit(size: Int, persons: Int) {
    if size > persons {
        print("Il y a encore de la place.")
    } else  if size == persons {
        print("Il n'y a plus de place...")
    } else {
        print("Il y a des gens debout !")
    }
}

canWeSit(size: 10, persons: 10) // Il n'y a plus de place...

Je suis certain que vous aviez réussi ! Félicitations !

Parlons é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'appelleavec . 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 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 motavec . 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 :p. 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 :

Les types

Vous ne pensiez pas que je n'allais pas vous faire un petit laïus sur les types :D !

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 parait é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.

Implémentation

En utilisant tout ça, on va pouvoir transformer notre programme pour que Joe puisse entrer exactement le nombre de bidons de lait, bottes de laine et pelotes de laine qu'il récupère.

Pour cela, il va falloir poser une question supplémentaire pour savoir la quantité récupérée. Cette quantité va devoir être convertie en un entier. Ensuite en ajoutant des paramètres à nos fonctions milkCows, harvest et mowSheep, on va pouvoir ajouter tout cela dans la grange. A vous de jouer !

// A vous de jouer









































//======================
// MARK: - Parameters
//======================

// L'argent de Joe
var money = 0.0

// La grange de Joe : [lait, blé, laine]
var barn = ["milk": 0, "wheat": 0, "wool": 0]

//======================
// MARK: - Activities
//======================
func feedAnimals() {
    money -= 4
}

func sell() {
    money += Double(barn["milk"]!) * 0.50
    money += Double(barn["wheat"]!) * 0.30
    money += Double(barn["wool"]!) * 1

    // On vide la grange
    barn = ["milk": 0, "wheat": 0, "wool": 0]
}


func milkCows(retrieving quantity: Int) {
    barn["milk"]! += quantity
}

func harvest(retrieving quantity: Int) {
    barn["wheat"]! += quantity
}

func mowSheep(retrieving quantity: Int) {
    barn["wool"]! += quantity
}

//======================
// MARK: - Conversation
//======================

func readQuantity(of type: String) -> Int? {
    // On demande la quantité désirée
    print("Combien de \(type)  avez vous récupéré ?")

    // On convertie 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
}

func addNewActivity() {
    print("Qu’avez-vous fait aujourd'hui?"
        + "\n1. 🥕  J’ai nourri mes animaux"
        + "\n2. 💰  J’ai vendu mes produits"
        + "\n3. 🐄  J’ai trait mes vaches"
        + "\n4. 🌾  J’ai moissonné"
        + "\n5. 🐑  J’ai tondu mes moutons")

    // On récupère la réponse de l'utilisateur
    if let choice = readLine() {
        switch choice {
        case "1": // Nourrir les animaux
            feedAnimals()
        case "2": // Vendre les produits
            sell()
        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)
            }
        default:
            print("Je ne comprends pas")
        }

        print("🎉  Super 🎉")
    }
}

func presentMenu() {
    print("Que voulez vous faire ?"
        + "\n1. 🤸‍♂️  Enregistrer une nouvelle activité"
        + "\n2. 🏦  Consulter ma banque"
        + "\n3. 🏠  Consulter ma grange")

    // On récupère la réponse de l'utilisateur
    if let choice = readLine() {
        switch choice {
        case "1": // Ajouter une nouvelle activité
            addNewActivity()
        case "2": // Consulter la banque
            print("Votre banque contient \(money) euros !")
        case "3": // Consulter la grange
            print("Votre grange contient :"
                + "\n🍼  \(barn["milk"]!) bidons de lait"
                + "\n🌾  \(barn["wheat"]!) bottes de blé"
                + "\n⚪️  \(barn["wool"]!) pelotes de laine")
        default:
            print("Je ne comprends pas")
        }
    }
}

// La boucle du programme
while true {
    presentMenu()
}

Alors reprenons tranquillement ce qui a changé. Tout d'abord on a transformé les trois fonctions pour leur ajouter un paramètrequantity :

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 tel 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 convertie 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 :

  1. 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.

  2. 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

  3. Souvenez-vous lorsque l'instructionreturnest appelé, 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 typeInt n'a pas été effectuée. Et c'est ce qu'on veut.

  4. 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.

  5. 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. Je vous redonne mon schéma pour le résumé :

  • 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

Example of certificate of achievement
Example of certificate of achievement