Tracks
/
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

Las clausuras en Swift son bloques de código autocontenidos a los que se les pueden pasar parámetros para activar su cálculo y devolver valores. Las clausuras también capturan valores de su entorno y los usan en sus cálculos. Como son autocontenidas, se pueden pasar de un lado a otro en un programa como cualquier otro valor, o asignarse a constantes y variables.

Las clausuras pueden sonar muy similares a las funciones de Swift; de hecho lo son, y por una buena razón. Las funciones en Swift son casos especiales de clausuras: se les exige tener un nombre y se definen con una sintaxis un poco distinta.

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

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

Esto define una expresión de clausura 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 clausura completa cada vez que se quiere ejecutar, las clausuras se pueden asignar a nombres, como 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 ve arriba, las clausuras, igual que las funciones normales, pueden omitir la palabra clave return si el cuerpo de la clausura es una sola expresión.

La mayoría de las veces, las clausuras se usan con funciones de orden superior, ya sea pasándolas como parámetros o devolviéndolas como resultado:

[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, Agente Double-Null0111.

Las fuerzas de UMBRA están causando problemas otra vez, y depende de ti frustrar su último plan. Nuestra división de inteligencia nos informa 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 siguen olvidando cómo desarmar sus bombas fétidas, implementaron un sistema que genera las instrucciones de desarme, en las que se indica el orden en que deben cortarse los cables a partir del número de identificación de la bomba.

Tu trabajo es escribir el código que te permita desarmar 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 distintos colores. Escribe un closure que reciba una tupla (String, String, String) y devuelva la tupla con el orden de los dos primeros elementos invertido. Asigna este closure al nombre flip.

Es importante tener en cuenta que el tipo debe ser ChangeClosure (o un tipo sendable); la definición del tipo está en el archivo 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 debe ser ChangeClosure (o un tipo sendable); la definición del tipo está en el archivo stub.

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

3. Implementa un generador de mezcla de cables

Para determinar 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 mezcla 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 cortarlos. El número de identificación está formado por ocho bits, y cada bit determinará si se deben intercambiar o rotar los cables.

Para cada bit del número de identificación, empezando por el bit de 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. Después de aplicar 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 una pestaña nuevas
Swift Exercism

¿Todo 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.