Практика 4. Практика к лекции 4
Цель: освоить функции Swift (метки аргументов, значения по умолчанию, вариативные параметры, inout), научиться работать с функциями как объектами первого класса, разобраться в замыканиях (closures) и их сокращённом синтаксисе, а также применить map, filter, reduce для функциональной обработки данных.
Рекомендации по выполнению:
- Каждое задание оформляйте в отдельном файле (например,
task1.swift). - Запускайте через
swift имя_файла.swiftна Linux. - Обращайте внимание на разницу между меткой аргумента (argument label) и именем параметра (parameter name).
- Старайтесь использовать trailing closure syntax там, где это уместно.
- Сравнивайте с аналогичными конструкциями Python (
lambda,map,filter) — это поможет понять подход Swift.
A. Разминка — базовые функции
- Возведение в степень. Напишите функцию
power(base:exponent:), которая возводит целое число в целую степень. Значениеexponentпо умолчанию —2:
func power(base: Int, exponent: Int = 2) -> Int { var result = 1 for _ in 0..<exponent { result *= base } return result}
print(power(base: 3)) // 9print(power(base: 2, exponent: 10)) // 1024print(power(base: 5, exponent: 3)) // 125Дополнительно: обработайте случай exponent == 0 (результат всегда 1) и отрицательные степени (верните 0 для целочисленного варианта, объясните почему в комментарии).
- Метки аргументов. Напишите функцию для приветствия с использованием различных меток:
func greet(_ person: String, from hometown: String) -> String { return "Привет, \(person)! Рады видеть гостя из \(hometown)."}
print(greet("Иван", from: "Москва"))// Привет, Иван! Рады видеть гостя из Москва.Напишите ещё две функции:
move(from start: String, to end: String) -> String— возвращает"Перемещение из <start> в <end>".repeat(_ text: String, times count: Int) -> String— повторяет строкуcountраз через пробел.
В комментарии объясните, зачем нужны метки аргументов и когда используется _.
B. Вариативные параметры и inout
- Объединение строк. Напишите функцию
joinStrings, принимающую вариативный параметр строк и разделитель (по умолчанию", "):
func joinStrings(_ strings: String..., separator: String = ", ") -> String { return strings.joined(separator: separator)}
print(joinStrings("Swift", "Python", "Go"))// Swift, Python, Go
print(joinStrings("a", "b", "c", separator: " - "))// a - b - c
print(joinStrings("Один"))// ОдинНапишите аналогичную функцию sum(_ numbers: Int...) -> Int и протестируйте: sum(1, 2, 3, 4, 5) → 15.
- inout-параметры. Напишите функции, использующие
inout:
// Обмен значениямиfunc swapValues(_ a: inout Int, _ b: inout Int) { let temp = a a = b b = temp}
var x = 10, y = 20print("До: x=\(x), y=\(y)")swapValues(&x, &y)print("После: x=\(x), y=\(y)")Дополнительно напишите:
clamp(_ value: inout Int, min: Int, max: Int)— ограничивает значение диапазоном.normalize(_ array: inout [Double])— нормализует массив (делит каждый элемент на максимальный).
Пример ожидаемого поведения:
var val = 150clamp(&val, min: 0, max: 100)print(val) // 100
var data = [2.0, 4.0, 6.0, 8.0, 10.0]normalize(&data)print(data) // [0.2, 0.4, 0.6, 0.8, 1.0]C. Функции как объекты первого класса
- Функции высшего порядка. Напишите функцию
applyOperation, принимающую два числа и функцию-операцию:
func add(_ a: Int, _ b: Int) -> Int { return a + b }func multiply(_ a: Int, _ b: Int) -> Int { return a * b }
func applyOperation(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int { return operation(a, b)}
print(applyOperation(5, 3, operation: add)) // 8print(applyOperation(5, 3, operation: multiply)) // 15Создайте массив операций let operations: [(Int, Int) -> Int] = [add, multiply, ...] и выведите результат каждой операции для чисел 12 и 4.
- applyToEach. Напишите функцию, принимающую
inout-массив и замыкание, применяющее преобразование к каждому элементу:
func applyToEach(_ array: inout [Int], transform: (Int) -> Int) { for i in 0..<array.count { array[i] = transform(array[i]) }}
var nums = [1, 2, 3, 4, 5]applyToEach(&nums) { $0 * $0 }print(nums) // [1, 4, 9, 16, 25]
applyToEach(&nums) { $0 + 10 }print(nums) // [11, 14, 19, 26, 35]D. Замыкания и сокращённый синтаксис
- Фабрика замыканий (makeMultiplier). Напишите функцию, возвращающую замыкание:
func makeMultiplier(factor: Int) -> (Int) -> Int { return { number in return number * factor }}
let double = makeMultiplier(factor: 2)let triple = makeMultiplier(factor: 3)
print(double(5)) // 10print(triple(7)) // 21print(triple(10)) // 30Дополнительно создайте:
makeAdder(value:)— возвращает замыкание, прибавляющееvalue.makeFormatter(prefix:suffix:)— возвращает замыкание(String) -> String, оборачивающее строку.
let addTen = makeAdder(value: 10)print(addTen(5)) // 15
let bracket = makeFormatter(prefix: "[", suffix: "]")print(bracket("hello")) // [hello]- Этапы сокращения замыкания. Для массива
[5, 2, 8, 1, 9, 3]отсортируйте по убыванию, постепенно сокращая синтаксис замыкания. Запишите все 5 этапов:
let numbers = [5, 2, 8, 1, 9, 3]
// Этап 1: полная формаlet sorted1 = numbers.sorted(by: { (a: Int, b: Int) -> Bool in return a > b})
// Этап 2: вывод типовlet sorted2 = numbers.sorted(by: { a, b in return a > b})
// Этап 3: неявный returnlet sorted3 = numbers.sorted(by: { a, b in a > b })
// Этап 4: сокращённые имена аргументовlet sorted4 = numbers.sorted(by: { $0 > $1 })
// Этап 5: trailing closurelet sorted5 = numbers.sorted { $0 > $1 }
// Убедитесь, что все варианты дают одинаковый результатprint(sorted1) // [9, 8, 5, 3, 2, 1]print(sorted2)print(sorted3)print(sorted4)print(sorted5)E. Функциональные цепочки — map, filter, reduce
- Обработка массива. Используя
map,filter,reduceс trailing closure, обработайте массив чисел 1…20:- Отберите числа, делящиеся на 3.
- Возведите их в квадрат.
- Найдите сумму.
let result = (1...20) .filter { $0 % 3 == 0 } .map { $0 * $0 } .reduce(0, +)
print(result)// Числа: 3, 6, 9, 12, 15, 18// Квадраты: 9, 36, 81, 144, 225, 324// Сумма: 819Дополнительные цепочки (реализуйте каждую):
- Массив строк
["hello", "world", "swift", "is", "great"]→ отобрать слова длиннее 4 букв → привести к верхнему регистру → объединить через" ". - Массив
[1, -2, 3, -4, 5, -6]→ оставить положительные → удвоить → найти произведение.
- Обработка студентов. Создайте массив кортежей студентов и обработайте его функциональными цепочками:
typealias Student = (name: String, grade: Double)
let students: [Student] = [ ("Анна", 4.8), ("Борис", 3.2), ("Вера", 4.5), ("Глеб", 2.9), ("Дарья", 3.8), ("Егор", 4.1), ("Жанна", 4.9), ("Захар", 3.5)]Используя filter, map, sorted, reduce, выведите:
- Список отличников (балл >= 4.5), отсортированный по убыванию балла.
- Средний балл всех студентов.
- Имена всех студентов одной строкой через запятую.
- Количество студентов с баллом ниже 3.5.
Ожидаемый вывод:
Отличники: Жанна (4.9), Анна (4.8), Вера (4.5)Средний балл: 3.96Все студенты: Анна, Борис, Вера, Глеб, Дарья, Егор, Жанна, ЗахарСтудентов с баллом < 3.5: 2F. Мини-проект — «Конвейер обработки данных»
- Создайте программу
pipeline.swift, реализующую конвейер (pipeline) обработки текстовых данных с использованием функций и замыканий:
typealias Transform = (String) -> StringРеализуйте следующие функции-трансформации:
trimWhitespace— удаляет пробелы по краям.lowercase— приводит к нижнему регистру.removeDigits— удаляет все цифры из строки.replaceSpaces(with:)— возвращает замыкание, заменяющее пробелы на указанный символ.
Напишите функцию pipeline, принимающую строку и массив трансформаций:
func pipeline(_ input: String, transforms: [Transform]) -> String { var result = input for transform in transforms { result = transform(result) } return result}Протестируйте:
let input = " Hello World 123 "
let result = pipeline(input, transforms: [ trimWhitespace, lowercase, removeDigits, replaceSpaces(with: "-")])
print(result) // hello-world-Добавьте:
- Функцию
makePipeline(_ transforms: Transform...) -> Transform, возвращающую одно замыкание, объединяющее все трансформации. - Протестируйте:
let clean = makePipeline(trimWhitespace, lowercase)→clean(" HeLLo ")→"hello".
G. Критерии оценивания
- Корректность компиляции и работы всех программ на Linux: 0–4 балла
- Правильное использование функций (метки, значения по умолчанию, вариативные, inout): 0–4 балла
- Корректная работа с замыканиями и функциональными цепочками (
map/filter/reduce): 0–4 балла - Оформление кода: комментарии, именование, читаемость, отсутствие дублирования: 0–4 балла
Максимум: 16 баллов. Бонус до +2 за задания из раздела «Дополнительно».
H. Дополнительно (по желанию)
- Напишите функцию
memoize<T: Hashable, U>(_ function: @escaping (T) -> U) -> (T) -> U, кэширующую результаты вызовов (мемоизация). Протестируйте на функции вычисления чисел Фибоначчи. - Реализуйте аналог
reduceвручную: функциюmyReduce, принимающую массив, начальное значение и замыкание-аккумулятор. - Сравните производительность: создайте массив из 1 000 000 элементов и сравните время выполнения цепочки
filter → map → reduceс эквивалентным ручным цикломfor-in.