Parcours
/
Swift
Swift
/
Exercices
/
Maître de la lasagne
Maître de la lasagne

Maître de la lasagne

Exercice d'apprentissage

Introduction

Valeurs par défaut des paramètres

Des valeurs par défaut de paramètres peuvent être fournies pour n'importe quel paramètre d'une fonction, en affectant une valeur au paramètre dans la liste des paramètres, après l'annotation de type du paramètre. Lorsqu'une valeur par défaut est spécifiée pour un paramètre, l'appelant de la fonction peut omettre ce paramètre lors de l'appel, et la valeur par défaut est utilisée à la place.

func greeting(name: String = "guest", duration: Int = 2) -> String {
  "Welcome, \(name). Enjoy your \(duration) night stay."
}

greeting(name: "Bobo", duration: 7)
// Returns "Welcome, Bobo. Enjoy your 7 night stay."
greeting(duration: 3)
// Returns "Welcome, guest. Enjoy your 3 night stay."
greeting(name: "Wynona")
// Returns "Welcome, Wynona. Enjoy your 2 night stay."
greeting()
// Returns "Welcome, guest. Enjoy your 2 night stay."

Paramètres variadiques

Les paramètres variadiques permettent à une fonction d'accepter zéro ou plusieurs valeurs d'un type donné pour un seul paramètre. On définit un paramètre variadique en ajoutant trois points (...) à l'annotation de type du paramètre.

Dans le corps de la fonction, Swift met ces valeurs à disposition sous la forme d'un tableau du type indiqué :

import Foundation

func geometricMean(_ numbers: Double...) -> Double {
    var total = 1.0
    for number in numbers {
        total *= number
    }
    return pow(total, 1.0 / Double(numbers.count))
}

let result = geometricMean(1, 2, 3, 4, 5)
// result is 2.605171084697352
Note

Si une fonction prend d'autres paramètres après un paramètre variadique, le premier paramètre qui suit le paramètre variadique doit avoir une étiquette d'argument explicite, pour que Swift sache où se termine la liste d'arguments variadiques.

Paramètres in-out

Dans le corps d'une fonction, les paramètres sont traités comme des constantes, et non comme des variables ; toute tentative de modifier la valeur d'un paramètre provoque une erreur de compilation. Si une fonction souhaite modifier la valeur d'un paramètre, elle doit utiliser un paramètre in-out pour rendre cette mutabilité explicite.

Pour utiliser des paramètres in-out, il faut être sûr de trois choses :

  1. La définition de la fonction doit marquer explicitement les paramètres in-out en écrivant le mot-clé inout entre le : et le nom du type dans l'annotation de type du paramètre.
  2. La valeur passée au paramètre in-out doit être déclarée comme une variable, et non comme une constante. De même, elle ne peut pas être un littéral.
  3. Le nom de la variable doit être précédé d'un & dans l'appel à la fonction, ce qui indique explicitement qu'elle est modifiable.

Une fois cela fait, la fonction crée une copie interne du paramètre in-out, la modifie comme indiqué dans le corps de la fonction, puis recopie la valeur modifiée dans la variable passée en argument lorsque la fonction se termine.

func updateVersion(_ record: inout (version: Int, title: String)) {
  record.version += 1
}

var dbRecord = (version: 2, title: "Exercism")
updateVersion(&dbRecord)

// dbRecord is now (3, "Exercism")
Il y a quelques règles supplémentaires à connaître concernant les paramètres in-out.

1. Dans une fonction qui prend des paramètres in-out, il est interdit de référencer la variable passée en argument comme paramètre in-out.
2. La même variable ne peut pas être passée comme plusieurs paramètres in-out dans la même fonction.
   Par exemple, `inoutFunc(&mutVar, &mutVar)` provoque une erreur du compilateur.

Fonctions imbriquées

En Swift, on peut définir des fonctions à l'intérieur d'autres fonctions. On les appelle des fonctions imbriquées.

Les fonctions imbriquées servent souvent de fonctions utilitaires privées, qui accomplissent des tâches précises pour la fonction qui les contient sans encombrer la portée extérieure :

func makeNumber(_ number: Int) -> Int {
    func double(_ x: Int) -> Int {
        x * 2
    }

    func increment(_ x: Int) -> Int {
        x + 1
    }

    return number.isMultiple(of: 2) ? increment(double(number)) : double(number)
}

print(makeNumber(3)) // Prints 6
print(makeNumber(4)) // Prints 9

Les fonctions imbriquées se comportent comme des fonctions classiques, mais leur visibilité se limite au corps de la fonction qui les contient.

Instructions

Dans cet exercice, tu vas écrire un peu plus de code pour t'aider à cuisiner ta délicieuse lasagne tirée de ton livre de cuisine préféré.

Tu as cinq tâches, toutes liées au temps passé à cuisiner la lasagne.

1. Permets de modifier le temps de cuisson prévu

Tu veux écrire une version améliorée de la fonction remainingMinutesInOven qui prend en paramètre le nombre de minutes écoulées depuis que la lasagne est au four et qui renvoie combien de minutes la lasagne doit encore rester au four, d'après le temps de cuisson prévu en minutes de la tâche précédente. Auparavant, tu t'appuyais sur une valeur constante, expectedMinutesInOven, qui venait de l'extérieur de la fonction remainingMinutesInOven. C'était à la fois un peu trop encombrant à ton goût et pas assez souple, car cela ne te permettait pas de modifier le temps de cuisson prévu.

Pour y voir plus clair, définis la fonction remainingMinutesInOven qui prend en paramètre le nombre de minutes écoulées depuis que la lasagne est au four, ainsi qu'un paramètre expectedMinutesInOven dont la valeur par défaut est 40. Ta fonction doit renvoyer combien de minutes la lasagne doit encore rester au four, d'après le temps de cuisson prévu en minutes de la tâche précédente.

remainingMinutesInOven(elapsedMinutes: 30)
// Returns 10
remainingMinutesInOven(elapsedMinutes: 30, expectedMinutesInOven: 75)
// Returns 45

2. Permets un nombre variable de couches

Tu avais auparavant écrit une fonction preparationTimeInMinutes qui prend en paramètre le nombre de couches que tu as ajoutées à la lasagne et qui renvoie combien de minutes tu as passées à préparer la lasagne, en supposant que chaque couche te prend 2 minutes à préparer. Cependant, cette fonction ne te satisfait pas vraiment, car tu dois compter toi-même le nombre de couches de ta lasagne avant de pouvoir l'appeler, et qui a le temps pour ça ?

Pour te simplifier la vie, écris une fonction, preparationTimeInMinutes, qui prend un paramètre variadique String, layers, dans lequel tu listes les différentes couches de ta lasagne. La fonction comptera alors le nombre de couches de ta lasagne et renverra le temps de préparation nécessaire.

preparationTimeInMinutes(layers: "sauce", "noodles")
// Returns 4
preparationTimeInMinutes(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns 24

3. Calcule les quantités de nouilles et de sauce

Avant de commencer à assembler ta lasagne, tu veux t'assurer que tu as assez de sauce et de nouilles pour construire la lasagne de tes rêves. Pour chaque couche de nouilles de ta lasagne, tu auras besoin de 3 nouilles. Pour chaque couche de sauce de ta lasagne, tu auras besoin de 0,2 litre de sauce.

Définis la fonction quantities qui prend un paramètre variadique String, layers, dans lequel tu listes les différentes couches de ta lasagne. La fonction déterminera alors la quantité de nouilles et de sauce nécessaire pour préparer ton plat. Le résultat doit être renvoyé dans un tuple de type (noodles: Int, sauce: Double).

quantities(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns (noodles: 9, sauce: 0.8)
quantities(layers: "sauce", "noodles", "sauce", "noodles", "meat", "noodles", "mozzarella")
// Returns (noodles: 9, sauce: 0.4)

4. Convertis les litres en onces liquides sur place

Malheureusement, le volume de la sauce que tu as sous la main est indiqué en onces liquides plutôt qu'en litres. Pour t'aider à savoir si tu as assez de sauce sous la main, écris une fonction, toOz, qui prend un paramètre tuple in-out, (noodles: Int, sauce: Double), et qui convertit la quantité de sauce en onces liquides. Note qu'il y a 33,814 onces liquides par litre.

var amount = (noodles: 9, sauce: 0.8)
toOz(&amount)

amount
// Returns (noodles: 9, sauce: 27.0512)

5. Détermine le meilleur vin à servir

Enfin, tu veux savoir quel type de vin servir avec ta lasagne. Tu as entendu dire qu'il faut servir du vin blanc s'il y a plus de mozzarella, de ricotta et de béchamel dans la lasagne que de viande et de sauce, et du vin rouge sinon. Écris une fonction redWine qui prend un paramètre variadique String, layers, dans lequel tu listes les différentes couches de ta lasagne. La fonction utilisera cinq fonctions imbriquées pour déterminer le nombre de couches de mozzarella, de ricotta, de béchamel, de sauce et de viande dans la lasagne, puis comparera ces nombres de couches pour déterminer s'il faut servir du vin rouge, en renvoyant true dans ce cas et false sinon.

redWine(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns true
redWine(layers: "sauce", "noodles", "béchamel", "meat", "mozzarella", "noodles", "ricotta", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns false
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Swift Exercism

Prêt à commencer Maître de la lasagne ?

Inscris-toi sur Exercism pour apprendre et maîtriser Swift avec 35 concepts116 exercices, et un vrai mentorat humain, le tout gratuitement.