Замикання в 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 такі незграбні, вони часто випадково зводять смердючі бомби у своїх лабораторіях та офісах. А оскільки вони постійно забувають, як знешкоджувати свої смердючі бомби, вони впровадили систему, яка генерує вказівки для знешкодження, де перелічено порядок, у якому потрібно перерізати дроти, виходячи з ідентифікаційного номера бомби.
Наше завдання - написати код, який дозволить знешкодити смердючу бомбу на балу, перш ніж вона вибухне.
На кожній смердючій бомбі є три дроти різних кольорів.
Напишіть замикання, яке приймає кортеж (String, String, String) і повертає кортеж, у якому перші два елементи поміняно місцями.
Присвойте це замикання імені flip.
Важливо зазначити, що тип має бути ChangeClosure (або sendable-типом); визначення типу наведено у файлі-заготовці.
Інакше код не скомпілюється.
flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")
Напишіть замикання, яке приймає кортеж (String, String, String) і повертає кортеж, у якому порядок дротів обернуто вліво.
Присвойте це замикання імені rotate.
Важливо зазначити, що тип має бути ChangeClosure (або sendable-типом); визначення типу наведено у файлі-заготовці.
rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")
Щоб визначити порядок, у якому потрібно перерізати дроти смердючої бомби, потрібно реалізувати функцію, яка генерує належну функцію перемішування на основі ідентифікаційного номера смердючої бомби.
Реалізуйте функцію:
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")
Зареєструйтеся на Exercism, щоб вивчати й опановувати Swift, а також 35 концепцій116 вправ та справжнє наставництво від людей, і все це безкоштовно.