Se togliamo tutti i nomi e le etichette dalla firma di una funzione Swift, quello che resta è il tipo della funzione.
func shoppingList(item: String, quantity: Int) -> String {
"You need to buy \(quantity) \(item)."
}
// shoppingList: (String, Int) -> String
func repeater(_ phrase: String, times: Int) -> String {
var result = ""
for _ in 1...times {
result += phrase
}
return result
}
// repeater: (String, Int) -> String
func printString(line: String) {
print(line)
}
// printString: (String) -> ()
Nota che i tipi delle prime due funzioni sono uguali, (String, Int) -> String, anche se le due firme sono diverse e le due funzioni funzionano in modo molto diverso. Entrambe prendono in input una String e un Int e restituiscono una String. E, come si vede dal tipo della terza funzione, anche se il corpo della funzione non sembra restituire nulla, in realtà restituisce implicitamente (), l'unico valore di tipo Void.
Questi tipi di funzione si possono usare allo stesso modo di qualsiasi altro tipo in Swift: per esempio, si possono usare nella dichiarazione di variabili o costanti. E dato che in Swift le funzioni sono valori, a queste costanti o variabili si possono assegnare funzioni già esistenti:
var stringAndIntToString: (String, Int) -> String
stringAndIntToString = shoppingList
stringAndIntToString("carrots", 3)
// => "You need to buy 3 carrots."
Come per le altre variabili e costanti, il valore che assegni deve essere del tipo corretto:
stringAndIntToString = printShoppingList
// Error: Cannot assign value of type '(String) -> ()' to type '(String, Int) -> String'
Dato che i tipi di funzione si possono usare ovunque si possano usare altri tipi, ne consegue che possono essere usati come tipi di parametro, passati ad altre funzioni e chiamati dall'interno di quelle funzioni.
func apply3(to str: String, function f: (String, Int) -> String) -> String {
f(str,3)
}
// apply3: ([String], (String, Int) -> String) -> [String]
apply3(to: "eggs", function: shoppingList)
// => "You need to buy 3 eggs."
apply3(to: "eggs", function: repeater)
// => "eggseggseggs"
Allo stesso modo, i tipi di funzione possono essere usati come tipi restituiti dalle funzioni. In altre parole, si possono scrivere funzioni che creano e restituiscono altre funzioni. Quando si creano queste funzioni, la funzione restituita può usare gli altri parametri passati alla funzione o le variabili locali create all'interno della funzione genitore.
func makeAdder(base: Int) -> (Int) -> Int {
func adder(_ i: Int) -> Int {
base + i
}
return adder
}
// makeAdder: (Int) -> (Int) -> Int
let add10 = makeAdder(base: 10)
// add10: (Int) -> Int
let subtract20 = makeAdder(base: -20)
// subtract10: (Int) -> Int
add10(5)
// => 15
subtract20(5)
// => -15
Salve, agente Doppio-Zero0111.
La sua missione, se deciderà di accettarla, è scrivere un paio di strumenti per aiutarla a infiltrarsi nel quartier generale dell'UMBRA (Unione Minacce Bastarde Realmente Atroci) e recuperare i piani del loro ultimo, atroce piano. Per portare a termine questa missione le serviranno due strumenti.
Una volta entrata in possesso dei piani segreti, dovrà proteggerli in modo che solo chi conosce la password possa recuperarli.
Per farlo, dovrà implementare la funzione protectSecret(_ secret: String, withPassword password: String) -> (String) -> String.
Questa funzione restituirà un'altra funzione che accetta come input delle possibili stringhe di password.
Se la password inserita è corretta, la nuova funzione restituisce il segreto nascosto; se invece la password non è corretta, la funzione restituisce "Sorry. No hidden secrets here."
let secretFunction = protectSecret("Steal the world's supply of french fries!", withPassword: "5up3r53cr37")
secretFunction("Open sesame")
// Returns "Sorry. No hidden secrets here."
secretFunction("5up3r53cr37")
// Returns "Steal the world's supply of french fries!"
Abbiamo scoperto che l'UMBRA basa tutte le combinazioni delle sue casseforti sul numero della stanza in cui si trovano. Le combinazioni delle casseforti si generano applicando ripetutamente una funzione speciale al numero della stanza. Quindi il primo numero della combinazione deriverà dall'applicazione della funzione al numero della stanza, il secondo dall'applicazione della funzione al primo numero, e il terzo dall'applicazione della funzione al secondo numero.
Tuttavia, l'UMBRA cambia regolarmente la funzione usata per generare le combinazioni, quindi il tuo strumento dovrà accettare sia un numero di stanza sia la funzione appropriata per generare la combinazione.
Implementa la funzione generateCombination(forRoom room: Int, usingFunction f: (Int) -> Int) -> (Int, Int, Int) per generare la combinazione che ti servirà.
func makeCombo(_ x: Int) -> Int {
(x * 13) % 256
}
generateCombination(forRoom: 227, usingFunction: makeCombo)
// Returns (135, 219, 31)
Iscriviti a Exercism per imparare e padroneggiare Swift con 35 concetti116 esercizi e il mentoring di persone reali, tutto gratis.