Os closures em Swift são blocos de código autónomos que podem receber parâmetros para desencadear o seu cálculo e devolver valores. Os closures também capturam valores do seu ambiente e usam-nos nos seus cálculos. Como são autónomos, podem ser passados de um lado para o outro num programa como qualquer outro valor, ou atribuídos a constantes e variáveis.
Os closures podem parecer muito com as funções de Swift, e realmente parecem, e por uma boa razão. Em Swift, as funções são casos especiais de closures: têm obrigatoriamente um nome e são definidas com uma sintaxe ligeiramente diferente.
Embora, tecnicamente, as funções em Swift sejam closures, quando as pessoas falam de «closures» em Swift estão a referir-se a expressões de closure.
As expressões de closure escrevem-se com uma lista de parâmetros, seguida de um tipo devolvido e da palavra-chave in, seguida do corpo do closure, tudo entre um par de chavetas:
{ (a: Double, b: Double) -> Double in
return (a + b) / 2.0
}
Isto define uma expressão de closure do tipo (Double, Double) -> Double, que pode ser executada de forma semelhante a uma função deste tipo, aplicando-a a parâmetros do tipo adequado:
{ (a: Double, b: Double) -> Double in
return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5
Como não é muito prático escrever a expressão de closure completa sempre que se quer executá-la, os closures podem ser atribuídos a nomes, como qualquer outro valor em Swift. Depois, podem ser chamados aplicando o nome aos parâmetros:
let mean = { (a: Double, b: Double) -> Double in
(a + b) / 2.0
}
mean(13.0, 100.0)
// returns 56.5
Como se vê acima, os closures, tal como as funções normais, podem omitir a palavra-chave return se o corpo do closure for uma única expressão.
Na maioria das vezes, os closures são usados com funções de ordem superior, quer passados como parâmetros, quer devolvidos como resultados:
[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 }
}
Olá de novo, Agente Double-Null0111.
As forças da UMBRA estão outra vez em ação, e cabe-te a ti frustrar o seu mais recente plano. O nosso departamento de informações informa-nos de que esses Minions estão a planear rebentar uma bomba fétida no baile do Governador.
Como os Minions são muito desastrados, muitas vezes armam acidentalmente as bombas fétidas nos seus laboratórios e escritórios. E, como continuam a esquecer-se de como desarmar as bombas fétidas, implementaram um sistema que gera as instruções de desarmamento, que indicam a ordem em que os fios devem ser cortados a partir do número de identificação da bomba.
A tua tarefa é escrever o código que te permitirá desarmar a bomba fétida no baile antes de ela rebentar.
Cada bomba fétida tem três fios de cores diferentes.
Escreve um closure que recebe um tuplo (String, String, String) e devolve o tuplo com a ordem dos dois primeiros elementos trocada.
Atribui este closure ao nome flip.
É importante notar que o tipo tem de ser o ChangeClosure (ou um tipo sendable); a definição do tipo é dada no ficheiro stub.
Caso contrário, o código não compila.
flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")
Escreve um closure que recebe um tuplo (String, String, String) e devolve o tuplo com a ordem dos fios rodada para a esquerda.
Atribui este closure ao nome rotate.
É importante notar que o tipo tem de ser o ChangeClosure (ou um tipo sendable); a definição do tipo é dada no ficheiro stub.
rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")
Para descobrires a ordem em que deves cortar os fios da bomba fétida, vais precisar de implementar uma função que gera a função de baralhamento adequada com base no número de identificação da bomba fétida.
Implementa a função:
makeShuffle(
flipper: @escaping ((String, String, String)) -> (String, String, String),
rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)
que recebe como parâmetros de entrada um closure que troca dois fios e um closure que roda os três fios, e devolve um closure. Este closure devolvido recebe o número de identificação da bomba fétida e a ordem dos três fios, e calcula depois a ordem em que os fios devem ser cortados. O número de identificação é composto por oito bits, e cada bit determina se os fios devem ser trocados ou rodados.
Para cada bit do número de identificação, começando pelo bit mais à direita, aplicas o closure flipper ao tuplo de fios se o bit for 0, e aplicas o closure rotator se for 1, obtendo o novo estado dos fios.
Depois de aplicares os closures adequados aos oito bits do número de identificação, o estado final dos fios é a ordem em que devem ser cortados.
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")
Inscreve-te no Exercism para aprenderes e dominares Swift com 35 conceitos116 exercícios, e mentoria humana real, tudo grátis.