Треки
/
Swift
Swift
/
Вправи
/
Таємний агент Double-Null0111
Таємний агент Double-Null0111

Таємний агент Double-Null0111

Навчальна вправа

Вступ

Типи функцій

Якщо прибрати всі назви та мітки з сигнатури функції Swift, залишиться тип цієї функції.

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

Зауважмо, що типи перших двох функцій однакові, (String, Int) -> String, хоча сигнатури різні й самі функції працюють зовсім по-різному. Обидві вони приймають String та Int як вхідні дані й повертають String. А як бачимо з типу третьої функції, хоча тіло функції начебто нічого не повертає, насправді воно неявно повертає (), єдине значення типу Void.

Ці типи функцій можна використовувати так само, як будь-який інший тип у Swift; наприклад, їх можна використовувати в оголошенні змінних або констант. А оскільки функції у Swift є значеннями, уже наявні функції можна присвоювати цим константам і змінним:

var stringAndIntToString: (String, Int) -> String

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

Як і з іншими змінними та константами, значення, яке ми присвоюємо, має бути правильного типу:

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

Типи функцій як типи параметрів

Оскільки типи функцій можна використовувати будь-де, де можна використовувати інші типи, з цього випливає, що їх можна використовувати як типи параметрів, передавати в інші функції та викликати всередині цих функцій.

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"

Типи функцій як типи повернення

Так само типи функцій можна використовувати як типи повернення для функцій. Інакше кажучи, можна писати функції, які створюють і повертають інші функції. Коли ми створюємо такі функції, повернена функція може використовувати інші параметри, передані у функцію, або локальні змінні, створені всередині батьківської функції.

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

Вказівки

Вітаємо, агенте Double-Null0111.

Наше завдання, якщо ми вирішимо його прийняти, - написати два інструменти, які допоможуть нам проникнути до штаб-квартири UMBRA (United Minions Being Really Awful) і добути плани їхньої останньої воістину жахливої оборудки. Для виконання цієї місії нам знадобляться обидва.

1. Захистімо знайдені секретні плани паролем

Щойно ми отримаємо секретні плани, їх потрібно буде захистити так, щоб дістати їх могли лише ті, хто знає пароль.

Для цього потрібно реалізувати функцію protectSecret(_ secret: String, withPassword password: String) -> (String) -> String. Ця функція поверне іншу функцію, яка приймає як вхідні дані можливі паролі у вигляді рядків тексту (англ. string). Якщо введений пароль правильний, нова функція повертає прихований секрет, а якщо пароль неправильний, функція повертає "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. Згенеруймо комбінацію, щоб відкрити їхній сейф

Ми дізналися, що UMBRA будує всі комбінації до своїх сейфів на основі номера кімнати, у якій вони розташовані. Комбінації до сейфів генеруються шляхом багаторазового застосування спеціальної функції до номера кімнати. Отже, перше число комбінації утворюється застосуванням функції до номера кімнати, друге - застосуванням функції до першого числа, а третє - застосуванням функції до другого числа.

Однак UMBRA регулярно змінює функцію, яку використовує для генерування комбінацій, тож наш інструмент має приймати і номер кімнати, і відповідну функцію, щоб згенерувати комбінацію.

Реалізуймо функцію generateCombination(forRoom room: Int, usingFunction f: (Int) -> Int) -> (Int, Int, Int), щоб згенерувати потрібну нам комбінацію.

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

generateCombination(forRoom: 227, usingFunction: makeCombo)
// Returns (135, 219, 31)
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Swift Exercism

Час розпочати Таємний агент Double-Null0111?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Swift, а також 35 концепцій116 вправ та справжнє наставництво від людей, і все це безкоштовно.