Parcours
/
Swift
Swift
/
Exercices
/
Lignes de journal
Lignes de journal

Lignes de journal

Exercice d'apprentissage

Introduction

Lorsque l'on modélise des données qui ne peuvent prendre qu'un ensemble précis d'états possibles, comme les jours de la semaine, les points cardinaux ou les boutons d'une manette, les types de base comme Int ou String présentent des inconvénients majeurs. Avec des entiers ou des strings, le code doit sans cesse vérifier qu'il n'y a pas de valeurs invalides (comme un code de statut 999 ou une faute de frappe comme "leftt"), et un lecteur humain ne peut pas facilement deviner ce que ces nombres représentent.

Swift résout ce problème avec les énumérations (souvent abrégées en enums). Une énumération définit un type commun à un groupe de valeurs nommées et apparentées. Comme le compilateur connaît à l'avance tous les cas possibles, il peut vérifier l'exactitude et l'exhaustivité du code à la compilation. Les énumérations peuvent aussi avoir des méthodes, des initialiseurs et des propriétés calculées.

Définis des énumérations

Définis une énumération avec le mot-clé enum suivi du nom du type, et liste ses cas entre accolades. Par convention en Swift, le nom du type s'écrit en UpperCamelCase, tandis que chaque cas s'écrit en lowerCamelCase :

enum NESButton {
    case up
    case down
    case left
    case right
    case a
    case b
    case select
    case start
}

Tu peux aussi définir plusieurs cas sur une seule ligne, séparés par des virgules :

enum NESButton {
    case up, down, left, right, a, b, select, start
}

Pour désigner un cas d'énumération, écris le nom du type de l'énumération, un point (.), puis le nom du cas. Une fois que le type est connu ou peut être déduit par le compilateur, tu peux omettre le nom du type et utiliser la notation pointée abrégée :

var lastPressed = NESButton.up

// Swift infers the type, so you can omit the type name:
lastPressed = .down

let konamiCode: [NESButton] = [.up, .up, .down, .down, .left, .right, .left, .right, .b, .a]

Méthodes et initialiseurs

Les énumérations peuvent définir des méthodes et des initialiseurs personnalisés, exactement comme les classes et les structures. À l'intérieur d'une méthode d'énumération, self désigne la valeur du cas courant :

enum Coin {
    case heads
    case tails

    init(_ value: Int) {
        if value.isMultiple(of: 2) {
            self = .heads
        } else {
            self = .tails
        }
    }

    func flip() -> Coin {
        switch self {
        case .heads: return .tails
        case .tails: return .heads
        }
    }
}

let tails = Coin(13)     // .tails
let heads = Coin(0)      // .heads
let flipped = heads.flip() // .tails

Valeurs brutes

Les cas d'une énumération peuvent être préremplis avec des valeurs par défaut appelées valeurs brutes. Toutes les valeurs brutes d'une énumération doivent être du même type (comme Int, String ou Character) :

enum NESButton: Character {
    case up = "⬆️"
    case down = "⬇️"
    case left = "⬅️"
    case right = "➡️"
    case a = "🅰️"
    case b = "🅱️"
    case select = "✅"
    case start = "🚦"
}

print(NESButton.left.rawValue) // Prints "⬅️"

Tu peux initialiser une énumération à partir d'une valeur brute avec init(rawValue:). Comme une valeur brute invalide peut être fournie, cet initialiseur renvoie un optionnel :

let upButton = NESButton(rawValue: "⬆️") // Optional(NESButton.up)
let invalid = NESButton(rawValue: "🙄")  // nil

Swift peut affecter automatiquement des valeurs brutes pour les énumérations de type String et Int :

  • Pour les énumérations de type String, la valeur brute de chaque cas vaut par défaut son nom.
  • Pour les énumérations de type Int, les valeurs brutes commencent à 0 et augmentent de 1, sauf indication contraire.
enum Planet: Int {
    case mercury = 1, venus, earth, mars
}

print(Planet.earth.rawValue) // Prints 3

Instructions

Dans cet exercice, on va traiter des lignes de log.

Chaque ligne de log est une string formatée comme suit : "[<LVL>]: <MESSAGE>".

Voici les différents niveaux de log :

  • TRC (trace)
  • DBG (debug)
  • INF (info)
  • WRN (warning)
  • ERR (error)
  • FTL (fatal)

Tu as trois tâches, qui doivent toutes être réalisées pour fournir la fonctionnalité nécessaire.

1. Créer l'énumération LogLevel

Définis une énumération LogLevel comportant six cas correspondant aux niveaux de log ci-dessus, plus un cas inconnu pour les niveaux dont le niveau de log est manquant ou non standard.

  • trace
  • debug
  • info
  • warning
  • error
  • fatal
  • unknown

2. Analyser le niveau de log

Ensuite, implémente l'initialiseur LogLevel pour analyser le niveau de log d'une ligne de log :

LogLevel("[INF]: File deleted")
// Returns LogLevel.info
LogLevel("Something went wrong!")
// Returns LogLevel.unknown

3. Convertir une ligne de log au format court

Le niveau de log d'une ligne de log est assez verbeux. Pour réduire l'espace disque nécessaire au stockage des lignes de log, un format court a été développé : "<ENCODED_LEVEL>:<MESSAGE>".

Le niveau de log encodé est une simple correspondance entre un niveau de log et un nombre :

  • trace - 0
  • debug - 1
  • info - 4
  • warning - 5
  • error - 6
  • fatal - 7
  • unknown - 42

Implémente la méthode shortFormat(message: String) -> String qui peut renvoyer le format raccourci d'une ligne de log sous forme de String :

let overflow = LogLevel.error
overflow.shortFormat(message: "Stack overflow")
// Returns "6:Stack overflow"
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Swift Exercism

Prêt à commencer Lignes de journal ?

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