Le closure in Swift sono blocchi di codice a sé stanti ai quali si possono passare dei parametri per attivarne il calcolo, e che possono restituire dei valori. Le closure inoltre catturano i valori dal loro ambiente e li usano nei loro calcoli. Poiché sono a sé stanti, possono essere passate da una parte all'altra di un programma come qualsiasi altro valore, oppure assegnate a costanti e variabili.
Le closure possono sembrare molto simili alle funzioni di Swift; in effetti lo sono, e per una buona ragione. Le funzioni in Swift sono casi particolari di closure: devono avere un nome e si definiscono con una sintassi leggermente diversa.
Sebbene in Swift le funzioni siano tecnicamente delle closure, quando si parla di «closure» in Swift ci si riferisce alle espressioni closure.
Un'espressione closure si scrive con un elenco di parametri seguito da un tipo restituito e dalla parola chiave in, seguita dal corpo della closure, il tutto racchiuso tra parentesi graffe:
{ (a: Double, b: Double) -> Double in
return (a + b) / 2.0
}
Questa definisce un'espressione closure di tipo (Double, Double) -> Double e può essere eseguita in modo simile a una funzione di questo tipo, applicandola a parametri del tipo appropriato:
{ (a: Double, b: Double) -> Double in
return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5
Dato che non è molto comodo scrivere ogni volta l'intera espressione closure quando la si vuole eseguire, le closure possono essere assegnate a un nome, come qualsiasi altro valore in Swift. A quel punto si possono chiamare applicando il nome ai parametri:
let mean = { (a: Double, b: Double) -> Double in
(a + b) / 2.0
}
mean(13.0, 100.0)
// returns 56.5
Come si è visto sopra, le closure, proprio come le funzioni normali, possono omettere la parola chiave return se il corpo della closure è una singola espressione.
Molto spesso le closure si usano con le funzioni di ordine superiore, passandole come parametri oppure restituendole come risultati:
[11, 75, 3, 99, 53].contains(where: { (x: Int) -> Bool in x > 100 })
// returns false
["apple", "ball", "carrot"].sorted(by: { (s1: String, s2: String) -> Bool in s1.count < s2.count })
// returns ["ball", "apple", "carrot"]
func makeAdder(base: Int) -> (Int) -> Int {
{ (x: Int) -> Int in base + x }
}
Ciao di nuovo, agente Double-Null0111.
Le forze di UMBRA si stanno muovendo di nuovo, e tocca a te sventare il loro ultimo piano. Il nostro reparto di intelligence ci informa che quei Minions hanno in programma di far scoppiare una bomba puzzolente al ballo del Governatore.
Dato che i Minions sono così maldestri, spesso armano per sbaglio le bombe puzzolenti nei loro laboratori e nei loro uffici. E dato che continuano a dimenticare come disinnescarle, hanno messo in piedi un sistema che genera le istruzioni di disinnesco, elencando l'ordine in cui i fili devono essere tagliati a partire dal numero ID della bomba.
Il tuo compito è scrivere il codice che ti permetterà di disinnescare la bomba puzzolente del ballo prima che esploda.
Ogni bomba puzzolente ha tre fili di colori diversi.
Scrivi una closure che prende una tupla (String, String, String) e restituisce la tupla con l'ordine dei primi due elementi scambiato.
Assegna questa closure al nome flip.
È importante notare che il tipo deve essere ChangeClosure (o un tipo sendable), la definizione del tipo è fornita nel file stub.
Altrimenti il codice non compilerà.
flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")
Scrivi una closure che prende una tupla (String, String, String) e restituisce la tupla con l'ordine dei fili ruotato verso sinistra.
Assegna questa closure al nome rotate.
È importante notare che il tipo deve essere ChangeClosure (o un tipo sendable), la definizione del tipo è fornita nel file stub.
rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")
Per capire in quale ordine tagliare i fili della bomba puzzolente, dovrai implementare una funzione che genera la giusta funzione di mescolamento in base al numero ID della bomba puzzolente.
Implementa la funzione:
makeShuffle(
flipper: @escaping ((String, String, String)) -> (String, String, String),
rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)
che prende in input una closure che scambia due fili ed una closure che ruota i tre fili, e restituisce una closure. Questa closure restituita prende il numero ID della bomba puzzolente e l'ordine dei tre fili, e poi calcola l'ordine in cui i fili devono essere tagliati. Il numero ID è composto da otto bit, ed ogni bit determina se scambiare o ruotare i fili.
Per ogni bit del numero ID, partendo da quello più a destra, applicherai la closure flipper alla tupla dei fili se il bit è 0, ed applicherai la closure rotator se è 1, ottenendo il nuovo stato dei fili.
Dopo aver applicato le closure appropriate per tutti gli otto bit dell'ID, lo stato finale dei fili è l'ordine in cui devono essere tagliati.
let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// Returns ([UInt8], (String, String, String)) -> (String, String, String)
shuffler([1, 0, 0, 1, 1, 0, 1, 1], ("red", "yellow", "blue"))
// Returns ("red", "blue", "yellow")
Iscriviti a Exercism per imparare e padroneggiare Swift con 35 concetti116 esercizi e il mentoring di persone reali, tutto gratis.