swift

Le enumerazioni, per chi programma in C++, sono un tipo di comune utilizzo dalla semplice comprensione: è un gruppo di costanti omogenee che possono essere utilizzate ad esempio nel costrutto switch per selezionare i vari case. Un esempio può essere il seguente:

enum fasiProduzione { Inizio, Ciclo1, Ciclo2, Fine, Attesa };
fasiProduzione fasi;

switch (fasi) {
  case Inizio: //Fai qualcosa
    break;
  case Ciclo1: //Fai qualcosa
    break;
  case Ciclo2 : //Fai qualcosa
    break;
  case Fine : //Fai qualcosa
    break;
  case Attesa :
    break;
}

In Swift le enumerazioni portano maggiori funzionalità. Per quanto la definizione fornita nel manuale Swift possa sembrare criptica: "Le enumerazioni definiscono un tipo comune per un gruppo di valori tra loro in relazione che permettono il loro utilizzo in modo sicuro (type-safe)"; in realtà prelude ad un utilizzo molto ampio delle enumerazioni, in cui vengono pensate come un ambiente sicuro dove effettuare operazioni.

In altre parole le enumerazioni non sono più soltanto un insieme di valori omogenei ma sono più vicine al concetto di classe nella quale possono esistere proprietà calcolate (computed properties) e metodi. Come per le classi le loro funzionalità possono essere estese tramite le extensions.

Nella loro esposizione più semplice le enumerazioni possono essere definite come nel seguente esempio:

enum FasiProduzione {

    case inizio

    case esecuzione

    case attesa

    case fine

}

 

Nell'esempio abbiamo un ciclo di produzione individuato dalle quattro fasi enumerate tramite l'istruzione case. Devo evidenziare che nelle enumerazioni in C o C++ ad ogni valore viene associato un intero che funge da indice; in Swift questo non è vero.

Le enumerazioni in Swift sono un vero e proprio tipo definito dall'utente, per questo motivo per la convenzione utilizzata in Swift il nome deve cominciare con una lettera maiuscola. Più in generale i nomi di tipi e protocolli seguono la convenzione "UpperCamelCase" (es. MioTipo), mentre tutto il resto "lowerCamelCase" (es. miaVariabile).

var faseInCorso = FasiProduzione.esecuzione

faseInCorso = .attesa

Una volta assegnato il tipo enumerativo a una variabile Swift, come nell'esempio sopra esposto alla variabile faseInCorso, riassegnando un valore alla variabile non è più necessario scrivere l'intero nome del tipo enumerativo, è sufficiente indicare il singolo case. Con i tipi enumerativi possiamo utilizzare il costrutto switch.

switch faseInCorso {

    case .inizio:

        print("inizio")

    

    case .esecuzione:

        print("esecuzione")

    

    case .attesa:

        print("attesa")

    

    case .fine:

        print("fine")

}

 

E' importante notare che se nel blocco switch non vengono elencati tutti i casi del tipo faseInCorso il compilatore restituisce un errore. Per evitare questo è necessario inserire il caso default. Un'altra annotazione importante è l'assenza dell'istruzione break dopo ogni case.

Riprendiamo la definizione enumerativa delle FasiProduzione data in precedenza e supponiamo di aver bisogno di un'informazione in più; supponiamo ad esempio che nella fase esecuzione si voglia sapere a quale livello di esecuzione siamo arrivati, oppure nella fase attesa quanti secondi mancano alla ripresa del lavoro, infine nella fase finale avere l'esito della produzione. Tutto questo può essere fatto all'interno del tipo enumerativo. Modifichiamo la definizione di FasiProduzione:

enum FasiProduzione {

    case inizio

    case esecuzione(String)

    case attesa(Int)

    case fine(String)

}

var faseInCorso = FasiProduzione.inizio

 

Con questa dichiarazione di FasiProduzione abbiamo imposto un vincolo di tipo a tre elementi. L'elemento esecuzione porta con se un'ulteriore informazione di tipo String, attesa invece il tipo int. Viene poi creata la variabile faseInCorso e gli viene assegnato il valore iniziale. Nel prossimo codice tramite switch daremo un significato a questi valori.

switch faseInCorso {

    case .inizio:

        //Imposto la variabile faseInCorso in modo che al prossimo loop

        //passo al case successivo, contestualmente gli associo la stringa

        //"Inizio assemblaggio".

        faseInCorso = .esecuzione("Inizio assemblaggio")

    case .esecuzione(let livello):

        //Qui al primo passaggio riassegno alla variabile faseInCorso
        //lo stesso case 
ma cambio il contenuto della stringa.
        //Tramite if eseguo un codice 
diverso in base alla stringa passata.

        if livello == "Inizio assemblaggio" {

            faseInCorso = .esecuzione("Fine assemblaggio")

        }

        else if livello == "Fine assemblaggio" {

            faseInCorso = .attesa(60)

        }

    case .attesa(var tempo):

        //... eseguo le azioni per decrementare la variabile tempo ...

        //quando giunge a zero imposto faseInCorso per passare al
        //case successivo

        if tempo == 0 {

            faseInCorso = .fine("Produzione OK")

        }

    case .fine(let risultato):

        if risultato == "Produzione OK" {

            print("fine")

        }

        else {

            print("Errore produzione")

        }

}

 

Supponiamo che ci sia una funzione esterna che chiami ripetutamente la porzione di codice sopra riportata. Ad ogni ciclo il valore della variabile faseInCorso viene aggiornato in modo che nel ciclo successivo venga eseguita una porzione diversa di codice. Si può capire che dopo un numero finito di cicli si arriverà al risultato di vedere stampato la parola "fine".

La cosa interessante è che durante il primo ciclo al tipo enumerativo FasiProduzione.esecuzione viene assegnato un valore stringa che è "Inizio assemblaggio". Nel ciclo successivo quando verrà il turno di .esecuzione il valore precedentemente assegnato viene recuperato con l'istruzione let e gli viene dato il nome livello. A questo punto livello viene interrogato per ricavarne il valore "Inizio assemblaggio" ed eseguire il codice relativo. Questa procedura viene ripetuta analogamente nei cicli successivi.

Riassumendo:

  • E' possibile assegnare ad ogni elemento di enum un proprio tipo.
  • E' possibile assegnare un valore a detto elemento tramite la variabile associata.
  • E' possibile richiamare il valore sopra assegnato tramite switch e le parole chiave let e var.