Треки
/
Swift
Swift
/
Вправи
/
Double-Null0111: Замикання назавжди
Double-Null0111: Замикання назавжди

Double-Null0111: Замикання назавжди

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

Вступ

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

Замикання можуть звучати дуже схоже на функції Swift; так і є, і на те є поважна причина. Функції в Swift - це окремі випадки замикань, які повинні мати назву та визначаються дещо іншим синтаксисом.

Хоча функції в Swift технічно є замиканнями, коли в Swift говорять про «замикання», то мають на увазі вирази замикання. Вирази замикання записуються як список параметрів, за яким іде тип повернення, ключове слово in, а далі тіло замикання, і все це в парі фігурних дужок:

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

Це визначає вираз замикання типу (Double, Double) -> Double, і його можна виконати подібно до функції цього типу, застосувавши його до параметрів відповідного типу:

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

Оскільки щоразу виписувати повний вираз замикання не дуже зручно, замикання можна присвоїти іменам, як і будь-яке інше значення в Swift. Потім їх можна викликати, застосувавши імʼя до параметрів:

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

mean(13.0, 100.0)
// returns 56.5

Як видно вище, замикання, як і звичайні функції, можуть опускати ключове слово return, якщо тіло замикання складається з єдиного виразу.

Найчастіше замикання використовують із функціями вищого порядку, або передаючи їх як параметри, або повертаючи як результат:

[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 }
}

Вказівки

Знову вітаємо, Agent Double-Null0111.

Сили UMBRA знову активізувалися, і саме нам належить зірвати їхній черговий план. Наш розвідувальний відділ повідомляє, що ті Minions планують підірвати смердючу бомбу на губернаторському балу.

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

Наше завдання - написати код, який дозволить знешкодити смердючу бомбу на балу, перш ніж вона вибухне.

1. Напишіть замикання, яке міняє місцями два дроти

На кожній смердючій бомбі є три дроти різних кольорів. Напишіть замикання, яке приймає кортеж (String, String, String) і повертає кортеж, у якому перші два елементи поміняно місцями. Присвойте це замикання імені flip.

Важливо зазначити, що тип має бути ChangeClosure (або sendable-типом); визначення типу наведено у файлі-заготовці. Інакше код не скомпілюється.

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

2. Напишіть замикання, яке обертає дроти

Напишіть замикання, яке приймає кортеж (String, String, String) і повертає кортеж, у якому порядок дротів обернуто вліво. Присвойте це замикання імені rotate.

Важливо зазначити, що тип має бути ChangeClosure (або sendable-типом); визначення типу наведено у файлі-заготовці.

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

3. Реалізуйте генератор перемішування дротів

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

Реалізуйте функцію:

makeShuffle(
  flipper: @escaping ((String, String, String)) -> (String, String, String),
  rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)

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

Для кожного біта в ідентифікаційному номері, починаючи з найправішого, потрібно застосувати замикання flipper до кортежу дротів, якщо біт дорівнює 0, і застосувати замикання rotator, якщо він дорівнює 1, отримуючи новий стан дротів. Після того як відповідні замикання застосовано для всіх восьми бітів ідентифікаційного номера, кінцевий стан дротів - це той порядок, у якому їх потрібно перерізати.

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")
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Swift Exercism

Час розпочати Double-Null0111: Замикання назавжди?

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