Parcours
/
Swift
Swift
/
Exercices
/
Double-Null0111 : les closures sont éternelles
Double-Null0111 : les closures sont éternelles

Double-Null0111 : les closures sont éternelles

Exercice d'apprentissage

Introduction

Les closures en Swift sont des blocs de code autonomes auxquels on peut passer des paramètres pour déclencher leur exécution et renvoyer des valeurs. Les closures capturent aussi des valeurs de leur environnement et les utilisent dans leurs calculs. Comme elles sont autonomes, on peut les faire circuler dans un programme comme n'importe quelle autre valeur, ou les affecter à des constantes et à des variables.

Les closures ressemblent beaucoup aux fonctions Swift, et ce n'est pas un hasard. Les fonctions en Swift sont des cas particuliers de closures : elles sont tenues d'avoir un nom et se définissent avec une syntaxe légèrement différente.

Bien que les fonctions en Swift soient techniquement des closures, quand on parle de « closures » en Swift, on désigne en réalité les expressions de closure. Une expression de closure s'écrit comme une liste de paramètres suivie d'un type de retour et du mot-clé in, puis du corps de la closure, le tout entre accolades :

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}

Cela définit une expression de closure de type (Double, Double) -> Double, que l'on peut exécuter de la même manière qu'une fonction de ce type, en lui appliquant des paramètres du type approprié :

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5

Comme il n'est pas très pratique d'écrire l'expression de closure complète chaque fois qu'on veut l'exécuter, on peut affecter une closure à un nom, comme n'importe quelle autre valeur en Swift. On peut ensuite l'appeler en appliquant ce nom aux paramètres :

let mean = { (a: Double, b: Double) -> Double in
  (a + b) / 2.0
}

mean(13.0, 100.0)
// returns 56.5

Comme on le voit ci-dessus, les closures, tout comme les fonctions ordinaires, peuvent omettre le mot-clé return lorsque le corps de la closure se réduit à une seule expression.

Le plus souvent, les closures s'emploient avec des fonctions d'ordre supérieur : on les passe en paramètres à ces fonctions, ou bien ces dernières les renvoient comme résultat :

[11, 75, 3, 99, 53].contains(where: { (x: Int) -> Bool in x > 100 })
// returns false

["apple", "ball", "carrot"].sorted(by: { (s1: String, s2: String) -> Bool in s1.count < s2.count })
// returns ["ball", "apple", "carrot"]

func makeAdder(base: Int) -> (Int) -> Int {
  { (x: Int) -> Int in base + x }
}

Instructions

Re-bonjour, Agent Double-Null0111.

Les forces d'UMBRA s'agitent à nouveau, et c'est à toi de déjouer leur dernier plan. Notre service de renseignement nous informe que ces Minions prévoient de faire exploser une bombe puante au bal du Gouverneur.

Comme les Minions sont très maladroits, il leur arrive souvent d'armer accidentellement les bombes puantes dans leurs labos et leurs bureaux. Et comme ils oublient sans cesse comment désamorcer leurs bombes puantes, ils ont mis en place un système qui génère les instructions de désamorçage indiquant l'ordre dans lequel les fils doivent être coupés, à partir du numéro d'identification de la bombe.

Ton travail consiste à écrire le code qui te permettra de désamorcer la bombe puante du bal avant qu'elle n'explose.

1. Écris une closure pour inverser deux fils

Chaque bombe puante possède trois fils de couleurs différentes. Écris une closure qui prend un tuple (String, String, String) et renvoie le tuple avec l'ordre des deux premiers éléments inversé. Affecte cette closure au nom flip.

Il est important de noter que le type doit être ChangeClosure (ou un type sendable) ; la définition du type est donnée dans le fichier stub. Sinon, le code ne compilera pas.

flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")

2. Écris une closure pour faire tourner les fils

Écris une closure qui prend un tuple (String, String, String) et renvoie le tuple avec les fils tournés vers la gauche. Affecte cette closure au nom rotate.

Il est important de noter que le type doit être ChangeClosure (ou un type sendable) ; la définition du type est donnée dans le fichier stub.

rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")

3. Implémente un générateur de mélange des fils

Pour déterminer dans quel ordre couper les fils de la bombe puante, tu vas devoir implémenter une fonction qui génère la fonction de mélange adaptée au numéro d'identification de la bombe puante.

Implémente la fonction :

makeShuffle(
  flipper: @escaping ((String, String, String)) -> (String, String, String),
  rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)

qui prend en entrée une closure qui inverse deux fils et une closure qui fait tourner les trois fils, et qui renvoie une closure. Cette closure renvoyée prend le numéro d'identification de la bombe puante et l'ordre des trois fils, puis calcule l'ordre dans lequel les fils doivent être coupés. Le numéro d'identification est composé de huit bits, et chaque bit détermine s'il faut inverser les fils ou les faire tourner.

Pour chaque bit du numéro d'identification, en commençant par le bit le plus à droite, tu appliques la closure flipper au tuple de fils si le bit vaut 0, et tu appliques la closure rotator s'il vaut 1, ce qui donne le nouvel état des fils. Une fois les closures appropriées appliquées pour les huit bits du numéro d'identification, l'état final des fils correspond à l'ordre dans lequel ils doivent être coupés.

let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// Returns ([UInt8], (String, String, String)) -> (String, String, String)
shuffler([1, 0, 0, 1, 1, 0, 1, 1], ("red", "yellow", "blue"))
// Returns ("red", "blue", "yellow")
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Swift Exercism

Prêt à commencer Double-Null0111 : les closures sont éternelles ?

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