Rutas
/
Swift
Swift
/
Ejercicios
/
El agente secreto Double-Null0111
El agente secreto Double-Null0111

El agente secreto Double-Null0111

Ejercicio de aprendizaje

Introducción

Tipos de función

Si se eliminaran todos los nombres y etiquetas de la firma de una función de Swift, solo quedaría el tipo de la función.

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) -> ()

Fíjate en que los tipos de las dos primeras funciones son el mismo, (String, Int) -> String, aunque las dos firmas son diferentes y las dos funciones funcionan de manera muy distinta. Ambas reciben un String y un Int como entrada y devuelven un String. Y, como se puede ver en el tipo de la tercera función, aunque el cuerpo de la función no parece devolver nada, en realidad devuelve implícitamente (), el único valor de tipo Void.

Estos tipos de función se pueden usar de la misma manera que cualquier otro tipo en Swift; por ejemplo, se pueden usar en la declaración de variables o constantes. Y, como las funciones son valores en Swift, las funciones ya existentes se pueden asignar a estas constantes o variables:

var stringAndIntToString: (String, Int) -> String

stringAndIntToString = shoppingList
stringAndIntToString("carrots", 3)
// => "You need to buy 3 carrots."

Al igual que con otras variables o constantes, el valor que le asignes debe ser del tipo correcto:

stringAndIntToString = printShoppingList
// Error: Cannot assign value of type '(String) -> ()' to type '(String, Int) -> String'

Tipos de función como tipos de parámetros

Como los tipos de función se pueden usar en cualquier lugar donde se pueda usar cualquier otro tipo, se deduce que se pueden usar como tipos de parámetros, pasarse a otras funciones y llamarse desde dentro de esas funciones.

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"

Tipos de función como tipos de retorno

Del mismo modo, los tipos de función se pueden usar como tipos de retorno de funciones. En otras palabras, se pueden escribir funciones que crean y devuelven otras funciones. Al crear estas funciones, la función que se devuelve puede usar los otros parámetros que se pasan a la función o las variables locales creadas dentro de la función padre.

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

Instrucciones

Hola, agente Double-Null0111.

Tu misión, si decides aceptarla, es escribir un par de herramientas que te ayuden a infiltrarte en el cuartel general de UMBRA (United Minions Being Really Awful) y recuperar los planos de su último plan realmente horrible. Necesitarás dos herramientas para llevar a cabo esta misión.

1. Protege los planes secretos recuperados con una contraseña

Una vez tengas los planes secretos, tendrás que protegerlos para que solo quienes conozcan la contraseña puedan recuperarlos.

Para ello, tendrás que implementar la función protectSecret(_ secret: String, withPassword password: String) -> (String) -> String. Esta función devolverá otra función que recibe posibles strings de contraseña como entrada. Si la contraseña introducida es correcta, la nueva función devuelve el secreto oculto; pero si la contraseña es incorrecta, la función devuelve «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!"

2. Genera una combinación para abrir su caja fuerte

Hemos descubierto que UMBRA basa todas las combinaciones de sus cajas fuertes en el número de la sala en la que se encuentran. Las combinaciones de las cajas fuertes se generan aplicando repetidamente una función especial al número de la sala. Así, el primer número de la combinación saldrá de aplicar la función al número de la sala; el segundo, de aplicar la función al primer número; y el tercero, de aplicar la función al segundo número.

Sin embargo, UMBRA cambia con regularidad la función que se usa para generar las combinaciones, así que tu herramienta tendrá que recibir tanto un número de sala como la función adecuada para generar la combinación.

Implementa la función generateCombination(forRoom room: Int, usingFunction f: (Int) -> Int) -> (Int, Int, Int) para generar la combinación que necesitarás.

func makeCombo(_ x: Int) -> Int {
  (x * 13) % 256
}

generateCombination(forRoom: 227, usingFunction: makeCombo)
// Returns (135, 219, 31)
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Swift Exercism

¿Listo para empezar El agente secreto Double-Null0111?

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