swift

Ho avuto un po' di difficoltà a digerire le closure, probabilmente sono ancora in fase di digestione. Il primo esempio portato nel manuale Swift 3.1 è piuttosto efficace e lo riporto di seguito dandogli però un percorso differente.

Il manuale definisce le closure in modo esatto (ovviamente) però una volta letta la definizione non si ha la sensazione di aver capito. Riflettendo a lungo su come spiegherei le closure ad un amico ho pensato che il modo più efficace sarebbe quello di dire che le closure sono un modo per riassumere in poco spazio delle funzioni che scritte per esteso richiederebbero parecchie righe di codice. Il riassunto che ne consegue mantiene comunque un livello di leggibilità che permette di capire il significato della funzione. Qui riporto l'esempio del manuale con la funzione prima e dopo la cura.

 

//Un array di stringhe da ordinare

let nomi = ["Paolo", "Carlo", "Antonella", "Elisabetta", "Marco"]

 

// Metodo senza closure. La funzione sorted è dell'array.

func ordineInverso(_ s1: String, s2: String) -> Bool {

    return s1 > s2

}

var nomiAlContrario = nomi.sorted(by: ordineInverso)

 

// Uguale ma tramite closure (la funzione ordineInverso non serve più)

var nomiAlContrario2 = nomi.sorted(by: >)

 

L'esempio ruota intorno alla funzione sorted che è un metodo del tipo array. Questa funzione prende come parametro un'altra funzione che ha lo scopo di indicare il modo in cui si vuole l'ordinamento dell'array. Nel primo esempio alla funzione sorted viene passata la funzione ordineInverso definita poco prima. La funzione ordineInverso è di tipo (String, String) -> Bool confronta due stringhe e restituisce true se la prima è superiore alla seconda. Questa risposta viene fornita tramite la comparazione con l'operatore '>'. La closure non fa altro (si fa per dire) che risparmiarci l'onere di scrivere tipi, valori di ritorno, intestazione della funzione per prendere l'unico elemento che davvero conta all'interno della funzione: l'operatore '>'. In pratica è il compilatore che riesce a dedurre quello che noi intendiamo mettendo solo il simbolo '>', ovvero la definizione della funzione è superflua. Questo è il messaggio del linguaggio Swift: semplificazione.

Una prima regola di carattere generale: le closure si applicano alle funzioni; sono particolarmente utili per semplificare le funzioni annidate.

// Liberamente ispirato dal video tutorial di Ray Wenderlich (www.raywenderlich.com)

 

// Prima definiamo il tipo closure

var multiplyClosure: (Int, Int) -> Int

 

// Poi implementiamo il corpo della closure in questo modo

multiplyClosure = { (a: Int, b: Int) -> Int in

    return a * b

}

// e anche in questo modo che equivale a quello sopra esposto

multiplyClosure = { $0 * $1 }

 

// Adesso usiamo la closure

let result = multiplyClosure(4, 3)

 

// Definiamo una nuova closure che somma invece di moltiplicare

var sumClosure: ( Int, Int ) -> Int = { $0 + $1 }

 

// Definiamo una funzione che prende come parametro un'altra funzione

func operationOnNumbers (_ a: Int, _ b: Int, _ operation: (Int, Int) -> Int) -> Int {

    return operation(a, b)

}

 

// Usiamo la funzione appena definita passando come parametro le closure

operationOnNumbers(3, 6, multiplyClosure)   //18

operationOnNumbers(3, 6, sumClosure)        //9

 

// Come sopra ma scrivendo la closure direttamente nella chiamata funzione

operationOnNumbers(12, 6, { $0 / $1 })      //2

 

In due parole. Definendo la multiplyClosure la parola chiave in serve a separare la dichiarazione delle variabili dal corpo della closure dove viene definita la funzione.

La closure viene ridefinita utilizzando la forma abbreviata. In questa forma si è fatta pulizia di tutte quelle parti inutili in quanto deducibili dal compilatore. Quindi sono state eliminate le parentesi, i tipi delle variabili, le stesse variabili e la funzione di ritorno del valore. I due parametri $0 e $1 rappresentano le due variabili a e b che non essendo state definite da noi vengono rappresentate da Swift in questa forma.