Quando modelliamo dati che possono assumere solo un insieme specifico di stati possibili, come i giorni della settimana, i punti cardinali o i pulsanti di un controller, i tipi di base come Int o String presentano grossi svantaggi.
Con i numeri interi o le stringhe, il codice deve controllare continuamente valori non validi (come un codice di stato 999 o un refuso come "leftt"), e chi legge il codice non riesce facilmente a capire cosa rappresentano i numeri.
Swift risolve questo problema con le enumerazioni (spesso abbreviate in enum). Un enum definisce un tipo comune per un gruppo di valori correlati e dotati di un nome. Poiché il compilatore conosce in anticipo tutti i casi possibili, può verificarne l'accuratezza e la completezza del codice in fase di compilazione. Gli enum possono anche avere metodi, inizializzatori e proprietà calcolate.
Definisci un enum usando la parola chiave enum seguita dal nome del tipo, con i casi elencati tra parentesi graffe.
Per convenzione di Swift, il nome del tipo usa UpperCamelCase, mentre ogni caso usa lowerCamelCase:
enum NESButton {
case up
case down
case left
case right
case a
case b
case select
case start
}
Puoi anche definire più casi su una sola riga, separati da virgole:
enum NESButton {
case up, down, left, right, a, b, select, start
}
Per fare riferimento a un caso di un enum, scrivi il nome del tipo dell'enum, un punto (.), e il nome del caso. Una volta che il tipo è noto o può essere inferito dal compilatore, puoi omettere il nome del tipo e usare la sintassi abbreviata con il punto:
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]
Gli enum possono definire metodi e inizializzatori personalizzati proprio come le classi e le struct. All'interno di un metodo di un enum, self si riferisce al valore del caso corrente:
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
I casi di un enum possono essere prepopolati con valori predefiniti chiamati valori grezzi.
Tutti i valori grezzi di un enum devono avere lo stesso tipo (come Int, String o 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 "⬅️"
Puoi inizializzare un enum a partire da un valore grezzo usando init(rawValue:). Poiché potrebbe essere fornito un valore grezzo non valido, questo inizializzatore restituisce un opzionale:
let upButton = NESButton(rawValue: "⬆️") // Optional(NESButton.up)
let invalid = NESButton(rawValue: "🙄") // nil
Swift può assegnare automaticamente i valori grezzi per gli enum di tipo String e Int:
String, il valore grezzo di ogni caso è, per impostazione predefinita, il nome del caso stesso.Int, i valori grezzi partono da 0 e aumentano di 1, a meno che non venga specificato diversamente.enum Planet: Int {
case mercury = 1, venus, earth, mars
}
print(Planet.earth.rawValue) // Prints 3
In questo esercizio elaborerai delle righe di log.
Ogni riga di log è una stringa formattata come segue: "[<LVL>]: <MESSAGE>".
Questi sono i diversi livelli di log:
TRC (trace)DBG (debug)INF (info)WRN (warning)ERR (error)FTL (fatal)Hai tre attività, ognuna delle quali deve essere completata per fornire la funzionalità necessaria.
Definisci un enum LogLevel con sei casi corrispondenti ai livelli di log sopra elencati, più un caso unknown per i livelli con livello di log mancante o non standard.
tracedebuginfowarningerrorfatalunknownPoi, implementa l'inizializzatore di LogLevel per analizzare il livello di log di una riga di log:
LogLevel("[INF]: File deleted")
// Returns LogLevel.info
LogLevel("Something went wrong!")
// Returns LogLevel.unknown
Il livello di log di una riga di log è piuttosto verboso.
Per ridurre lo spazio su disco necessario per memorizzare le righe di log, è stato sviluppato un formato breve: "<ENCODED_LEVEL>:<MESSAGE>".
Il livello di log codificato è una semplice mappatura da un livello di log a un numero:
trace - 0
debug - 1
info - 4
warning - 5
error - 6
fatal - 7
unknown - 42
Implementa il metodo shortFormat(message: String) -> String che può restituire il formato abbreviato della riga di log come String:
let overflow = LogLevel.error
overflow.shortFormat(message: "Stack overflow")
// Returns "6:Stack overflow"
Iscriviti a Exercism per imparare e padroneggiare Swift con 35 concetti116 esercizi e il mentoring di persone reali, tutto gratis.