Практика 6. Практика к лекции 6
Цель: закрепить безопасную работу с отсутствием значений в Swift — опциональные типы (Optional), принудительное извлечение, if let, guard let, optional chaining, оператор ??, compactMap — через серию упражнений от базовых к проектным.
Рекомендации по выполнению:
- Выполняйте задания в отдельных файлах
.swift(например,task1.swift,task2.swift). - Запускайте через терминал:
swift task1.swift(илиswift имя_файла.swift). - Для проектов из нескольких файлов используйте Swift Package Manager (
swift package init --type executable). - Работа ведётся на Linux — Xcode не требуется, достаточно установленного Swift toolchain.
- Избегайте
!(force unwrapping) во всех заданиях, кроме случаев, где это явно указано.
A. Разминка
- Напишите функцию
safeDivide(_ a: Double, _ b: Double) -> Double?, возвращающую результат деления илиnilпри делении на ноль.
print(safeDivide(10, 3) as Any) // Optional(3.3333333333333335)print(safeDivide(10, 0) as Any) // nilДополнительно: используйте if let для красивого вывода результата — "10 / 3 = 3.33" или "Деление на ноль".
- Напишите функцию
firstPositive(_ numbers: [Int]) -> Int?, возвращающую первое положительное число илиnil, если таких нет.
print(firstPositive([-3, -1, 0, 4, 7]) as Any) // Optional(4)print(firstPositive([-3, -1, 0]) as Any) // nilПодсказка: используйте метод first(where:).
B. if let и guard let
- Дан словарь
[String: String]с данными пользователя. Напишите функциюgreetUser(_ data: [String: String]), которая с помощьюguard letизвлекает ключи"name"и"age"(преобразуя значение возраста вInt), и выводит приветствие или сообщение об ошибке.
greetUser(["name": "Олег", "age": "22"]) // Привет, Олег! Тебе 22 лет.greetUser(["name": "Анна"]) // Ошибка: неполные данныеgreetUser(["name": "Иван", "age": "abc"]) // Ошибка: неполные данные- Напишите функцию
parseConfig(_ raw: [String: String]) -> (host: String, port: Int, path: String), которая извлекает из словаря ключи"host","port"(преобразуя вInt) и"path". Если хотя бы одного ключа нет илиportне является числом, верните значения по умолчанию:("localhost", 8080, "/"). Используйте??(nil-coalescing).
let config1 = ["host": "example.com", "port": "3000", "path": "/api"]let config2 = ["host": "myserver.ru"]
print(parseConfig(config1)) // ("example.com", 3000, "/api")print(parseConfig(config2)) // ("myserver.ru", 8080, "/")C. Optional chaining
- Создайте три структуры с опциональными вложенными свойствами:
struct Employee { let name: String let position: String?}
struct Department { let title: String var head: Employee?}
struct Company { let name: String var department: Department?}Создайте несколько экземпляров Company — с полностью заполненными данными и с nil на разных уровнях. Используя optional chaining и ??, безопасно выведите должность руководителя отдела:
let company1 = Company(name: "TechCorp", department: Department(title: "iOS", head: Employee(name: "Алиса", position: "Lead")))let company2 = Company(name: "StartupX", department: nil)
print(company1.department?.head?.position ?? "Неизвестно") // Leadprint(company2.department?.head?.position ?? "Неизвестно") // Неизвестно- Дан массив опциональных словарей
[ [String: String]? ]. С помощью optional chaining извлеките значение по ключу"email"из каждого словаря. Соберите все найденные email-адреса в массив (безnil). ИспользуйтеcompactMap.
let records: [[String: String]?] = [ ["name": "Анна", "email": "anna@mail.ru"], nil, ["name": "Борис"], ["name": "Виктор", "email": "victor@gmail.com"]]// Ожидаемый результат: ["anna@mail.ru", "victor@gmail.com"]D. compactMap и преобразования
- С помощью
compactMapпреобразуйте массив строк в[Double], отбросив некорректные значения. Затем найдите среднее арифметическое с помощьюreduce.
let data = ["3.14", "abc", "2.71", "", "1.0", "not_a_number", "42"]// Валидные: [3.14, 2.71, 1.0, 42.0]- Напишите функцию
safeIntArray(_ strings: [String]) -> [Int], которая принимает массив строк и возвращает массив целых чисел, безопасно преобразуя каждую строку (используяcompactMapиInt.init). Затем используйтеguardдля проверки, что массив не пуст, и выведите минимальное и максимальное значения.
safeIntArray(["10", "abc", "20", "", "5"])// Результат: [10, 20, 5]// Мин: 5, Макс: 20
safeIntArray(["abc", "xyz"])// Результат: [] — нет корректных чиселE. Практические паттерны
- Напишите функцию
findUser(byId id: Int, in database: [[String: Any]]) -> String, которая ищет пользователя по"id"в массиве словарей. Функция должна:- Использовать
first(where:)для поиска. - Использовать
guard letдля извлечения"name"из найденного словаря. - Возвращать
"Пользователь: <имя>"или"Не найден".
- Использовать
let users: [[String: Any]] = [ ["id": 1, "name": "Алиса", "age": 25], ["id": 2, "name": "Борис"], ["id": 3, "age": 30] // нет имени]
print(findUser(byId: 1, in: users)) // Пользователь: Алисаprint(findUser(byId: 3, in: users)) // Не найден (имя отсутствует)print(findUser(byId: 9, in: users)) // Не найденF. Мини-проект
- «Телефонная книга» — реализуйте систему управления контактами с активным использованием опционалов:
struct Contact { let name: String var phone: String? var email: String? var company: String?}Реализуйте структуру PhoneBook со следующим функционалом:
add(_ contact: Contact)— добавить контакт.find(byName name: String) -> Contact?— найти контакт по имени.allEmails() -> [String]— массив всех email-адресов (безnil), используяcompactMap.contactsWithPhone() -> [Contact]— контакты, у которых заполнен телефон.summary(for name: String) -> String— строка вида"Имя: X, Телефон: Y, Email: Z", где отсутствующие данные заменены на"не указано"(через??).
Пример использования:
var book = PhoneBook()book.add(Contact(name: "Анна", phone: "+79001234567", email: "anna@mail.ru", company: "TechCorp"))book.add(Contact(name: "Борис", phone: nil, email: "boris@gmail.com", company: nil))book.add(Contact(name: "Виктор", phone: "+79007654321", email: nil, company: nil))
print(book.find(byName: "Анна") as Any)// Optional(Contact(name: "Анна", phone: Optional("+79001234567"), ...))
print(book.allEmails())// ["anna@mail.ru", "boris@gmail.com"]
print(book.summary(for: "Виктор"))// Имя: Виктор, Телефон: +79007654321, Email: не указаноКритерии оценивания
- Корректность и полнота реализации: 0–5 баллов
- Грамотное использование опционалов (
if let,guard let,??, optional chaining,compactMap): 0–5 баллов - Качество и читаемость кода (именование, отсутствие force unwrapping без необходимости): 0–3 балла
- Наличие проверочных
print/assertвыражений: 0–3 балла
Максимум: 16 баллов. Бонус до +2 за дополнительные задания.
Дополнительно (по желанию)
- В мини-проекте добавьте метод
merge(_ other: PhoneBook), объединяющий две телефонные книги с устранением дубликатов по имени (при конфликте — сохранять запись, у которой заполнено больше полей). - Реализуйте функцию
unwrapOrThrow<T>(_ optional: T?, error: String) throws -> T, которая бросает ошибку, если значениеnil. - Сравните подходы к обработке отсутствия значений в Swift (
Optional) и Python (None/ исключения) — напишите короткий комментарий в коде.