Rutas
/
Swift
Swift
/
Ejercicios
/
Double-Null0111: las closures son para siempre
Double-Null0111: las closures son para siempre

Double-Null0111: las closures son para siempre

Ejercicio de aprendizaje

Introducción

Los closures en Swift son bloques de código autocontenidos a los que se les pueden pasar parámetros para desencadenar su cómputo y devolver valores. Los closures también capturan valores de su entorno y los usan en sus cómputos. Como son autocontenidos, se pueden pasar de un sitio a otro en un programa como cualquier otro valor, o asignarlos a constantes y variables.

Los closures pueden sonar mucho a las funciones de Swift; y es así, y con razón. Las funciones en Swift son casos especiales de closures que deben tener un nombre y se definen con una sintaxis ligeramente distinta.

Aunque las funciones en Swift son técnicamente closures, cuando la gente habla de «closures» en Swift, se refiere a las expresiones de closure. Las expresiones de closure se escriben como una lista de parámetros seguida de un tipo devuelto y la palabra clave in seguida del cuerpo del closure, todo ello entre un par de llaves:

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}

Esto define una expresión de closure de tipo (Double, Double) -> Double, y se puede ejecutar de forma similar a una función de este tipo, aplicándola a parámetros del tipo adecuado:

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5

Como no es muy cómodo escribir la expresión de closure completa cada vez que se quiere ejecutar, los closures se pueden asignar a nombres, igual que cualquier otro valor en Swift. Después, se pueden llamar aplicando el nombre a los parámetros:

let mean = { (a: Double, b: Double) -> Double in
  (a + b) / 2.0
}

mean(13.0, 100.0)
// returns 56.5

Como se ha visto antes, los closures, igual que las funciones normales, pueden omitir la palabra clave return si el cuerpo del closure es una única expresión.

Lo más habitual es usar los closures con funciones de orden superior, ya sea pasándolos como parámetros o devolviéndolos 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 }
}

Instrucciones

Hola de nuevo, Agent Double-Null0111.

Las fuerzas de UMBRA están causando problemas otra vez, y te toca a ti frustrar su último plan. Nuestro servicio de inteligencia nos informa de que esos Minions están planeando hacer estallar una bomba fétida en el baile del gobernador.

Como los minions son tan torpes, a menudo arman las bombas fétidas por accidente en sus laboratorios y oficinas. Y como no dejan de olvidar cómo desactivar sus bombas fétidas, han implementado un sistema que genera las instrucciones de desactivación, en las que se indica el orden en que hay que cortar los cables a partir del número de identificación de la bomba.

Tu trabajo es escribir el código que te permita desactivar la bomba fétida del baile antes de que estalle.

1. Escribe un closure para intercambiar dos cables

Cada bomba fétida tiene tres cables de colores diferentes. Escribe un closure que reciba una tupla (String, String, String) y devuelva la tupla con el orden de los dos primeros elementos intercambiado. Asigna este closure al nombre flip.

Es importante tener en cuenta que el tipo tiene que ser el ChangeClosure (o un tipo sendable); la definición del tipo se encuentra en el fichero stub. De lo contrario, el código no compilará.

flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")

2. Escribe un closure para rotar los cables

Escribe un closure que reciba una tupla (String, String, String) y devuelva la tupla con el orden de los cables rotado hacia la izquierda. Asigna este closure al nombre rotate.

Es importante tener en cuenta que el tipo tiene que ser el ChangeClosure (o un tipo sendable); la definición del tipo se encuentra en el fichero stub.

rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")

3. Implementa un generador de barajado de cables

Para averiguar el orden en que hay que cortar los cables de la bomba fétida, tendrás que implementar una función que genere la función de barajado adecuada a partir del número de identificación de la bomba fétida.

Implementa la función:

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 recibe como entrada un closure que intercambia dos cables y un closure que rota los tres cables, y devuelve un closure. Este closure devuelto recibe el número de identificación de la bomba fétida y el orden de los tres cables, y luego calcula el orden en que hay que cortar los cables. El número de identificación se compone de ocho bits, y cada bit determinará si hay que intercambiar o rotar los cables.

Para cada bit del número de identificación, empezando por el bit más a la derecha, aplicarás el closure flipper a la tupla de cables si el bit es 0, y aplicarás el closure rotator si es 1, lo que da el nuevo estado de los cables. Una vez aplicados los closures correspondientes a los ocho bits del número de identificación, el estado final de los cables es el orden en que hay que cortarlos.

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")
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Swift Exercism

¿Listo para empezar Double-Null0111: las closures son para siempre?

Regístrate en Exercism para aprender y dominar Swift con 35 conceptos116 ejercicios y mentoría humana real, todo gratis.