swift

Riprendiamo le descrizione dei tipi enumerativi.

Gli elementi che costituiscono l'insieme enum, oltre a poter avere un tipo proprio possono venire inizializzati con dei "raw data" (dati grezzi); vediamo un esempio con il tipo enum FasiProduzione:

enum FasiProduzione: Int {

    case inizio = 0

    case esecuzione = 1

    case fine = 2

}

 

Le differenze rispetto alle precedenti definizioni sono due:

  1. FasiProduzione viene definito di tipo Int.
  2. I case hanno tutti lo stesso tipo.

E' da notare che il raw data assegnato ad ogni case deve essere univoco: nell'esempio è un numero progressivo. Il tipo enumerativo sopra definito assume le sembianze dei medesimi tipi definiti in linguaggi come C o C++. Analogamente una volta definito il raw type del tipo enumerativo (nell'esempio Int) e definito il valore di partenza (nell'esempio inizio = 0) non è necessario inserire i valori successivi in quanto vengono dedotti in modo automatico.

Continuando ad approfondire l'argomento raw data / raw type, un tipo enumerativo così definito:

enum Direzioni: String {

    case nord, est, sud, ovest

}

 

associa a nord il raw data "nord", est il raw data "est" e così via, questo è reso possibile dall'aver attribuito il raw type String al tipo enum; questo si chiama assegnazione implicita del raw type. Un'altro particolare da evidenziare è la possibilità di raggruppare sotto un unico case l'intera lista di opzioni (come fatto sopra).

Per recuperare il raw value si usa l'omonima proprietà:

let miaDirezione = direzioni.est.rawValue //miaDirezione = "est"

 

Per creare una nuova istanza della enumerazione inizializzandola con uno specifico elemento della enumerazione si può utilizzare il metodo rawValue:

let faseProduzione = FasiProduzione(rawValue: 1) //faseProduzione = (optional)esecuzione

 

E' evidente che questa inizializzazione non è esente da errori in quanto si potrebbe specificare un raw value non esistente, per questo il valore restituito è un valore opzionale, se si specifica un raw value inesistente viene restituito nil.

let posizioneInesistente = 8

if let unaFase = FasiProduzione(rawValue: posizioneInesistente) {

    switch unaFase {

    case .inizio:

        print("Inizio")

    default:

        print("Altra fase")

    }

}

else {

    print("Fase della produzione inesistente")

}

 

Nell'esempio sopra esposto si usa l'optional binding per determinare se la posizione specificata esiste. Viene definita la costante unaFase creando un'istanza del valore enumerativo FasiProduzione in posizione 8. Se questa assegnazione desse esito positivo, cioè esistesse il valore con posizione 8, il codice contenuto in if verrebbe eseguito. Siccome, non esistendo, il metodo rawValue restituisce nil, la condizione if risulta false e il codice contenuto in switch non viene eseguito.

Infine abbiamo l'enumerazione ricorsiva. L'enumerazione è ricorsiva quando i casi enumerati hanno come valore associato l'istanza di se stessa. In questo caso è necessario indicarlo al compilatore facendo precedere a case la parola indirect. Vediamo un esempio:

enum EspressioneAritmetica {

    case numero(Int)

    indirect case somma(EspressioneAritmetica, EspressioneAritmetica)

    indirect case moltiplicazione(EspressioneAritmetica, EspressioneAritmetica)

}

 

Qui si vede che somma ha due operandi che potrebbero essere due numeri, ma potrebbero anche essere due altre espressioni; è il caso delle espressioni nidificate ed è il tipico caso di una ricorsione. 

L'enumerazione sopra esposta può assumere tre tipi di espressione aritmetica: un semplice numero, una somma e una moltiplicazione. Un'espressione aritmetica può essere di questo tipo (5+4)*2 dove troviamo una espressione aritmetica a sinistra del segno * e un numero a destra. Siamo di fronte ad una espressione aritmetica nidificata.

Queste sono le costanti opportunamente definite tramite il tipo enumerativo per rappresentare l'espressione (5+4)*2:

let cinque = EspressioneAritmetica.numero(5)

let quattro = EspressioneAritmetica.numero(4)

let somma = EspressioneAritmetica.somma(cinque, quattro)

let prodotto = EspressioneAritmetica.moltiplicazione(somma, EspressioneAritmetica.numero(2))

Attraverso la funzione calcola di seguito riportata si estraggono i valori numerici precedentemente inseriti nel tipo enumerativo calcolando il risultato dell'espressione.

func calcola (_ espressione: EspressioneAritmetica) -> Int {

    

    switch espressione {

    case let .numero(valore):

        return valore

        

    case let .somma(primo, secondo):

        return calcola(primo) + calcola(secondo)

        

    case let .moltiplicazione(primo, secondo):

        return calcola(primo) * calcola(secondo)

    }

}

print(calcola(prodotto)) //Stampa 18

 

La funzione è di tipo (EspressioneAritmetica) -> Int, l'underscore prima delletichetta espressione: permette di scrivere calcola(prodotto) in modo abbreviato come si vede all'interno della fuzione print(). Rimando allo specifico capitolo del manuale Swift 3.1 (Function Argument Labels and Parameter Names) per approfondire la logica nell'attribuire opportune etichette alle funzioni.

Il blocco switch valuta di volta in volta il parametro espressione passato; essendo un tipo enumerativo ricorsivo questa funzione verrà invocata più volte in quanto il parametro passato (prodotto) è composto da più elementi di tipo EspressioneAritmetica. Quando la funzione si troverà di fronte ad una espressione aritmentica composta da un solo valore numerico, è il caso delle costanti cinque e quattro, si limiterà a restituire il valore passato; quando, come nel caso della somma o moltiplicazione, si troverà davanti a espressioni aritmetiche restituirà rispettivamente la somma e il prodotto. A scanso di equivoci: la funzione viene chiamata una sola volta dalla funzione print(); è l'impostazione ricorsiva del tipo enumerativo che fa svolgere al compilatore dietro le quinte tutto il lavoro.

Un'ultima precisazione. La parola chiave let è stata usata in posizione diversa rispetto all'uso fatto nel paragrafo introduttivo in pratica let .moltiplicazione(primo, secondo) equivale a .moltiplicazione(let primo, let secondo).