Перейти к содержимому

Практика 3. Практика к лекции 3

Цель: освоить операторы Swift (арифметические, сравнения, логические, nil-coalescing, диапазоны) и управляющие конструкции (if/else, switch, for-in, while, repeat-while, guard, labeled statements, stride). Научиться выбирать подходящую конструкцию для конкретной задачи.

Рекомендации по выполнению:

  • Каждое задание оформляйте в отдельном файле (например, task1.swift).
  • Запускайте через swift имя_файла.swift на Linux.
  • Обращайте внимание на то, что switch в Swift не требует break (нет «проваливания») и требует исчерпывающего покрытия случаев.
  • Используйте guard вместо вложенных if для раннего выхода — это идиоматичный Swift.

A. Разминка — операторы

  1. Арифметика и остатки. Напишите программу, которая для двух чисел a = 23 и b = 7 выводит результаты всех арифметических операций, включая остаток от деления и вещественное деление:
let a = 23
let b = 7
print("\(a) + \(b) = \(a + b)")
print("\(a) - \(b) = \(a - b)")
print("\(a) * \(b) = \(a * b)")
print("\(a) / \(b) = \(a / b)") // целочисленное
print("\(a) % \(b) = \(a % b)") // остаток
print("\(a) / \(b) = \(Double(a) / Double(b))") // вещественное

Дополнительно: объясните в комментарии, чем % в Swift отличается от % в Python для отрицательных чисел (проверьте -7 % 3).

  1. Nil-coalescing и Optional. Напишите программу, демонстрирующую оператор ??:
let userInput: String? = nil
let name = userInput ?? "Гость"
print("Привет, \(name)!") // Привет, Гость!
let parsed: Int? = Int("abc")
let value = parsed ?? 0
print("Значение: \(value)") // Значение: 0

Создайте массив let inputs: [String?] = ["42", nil, "hello", "100", nil]. Для каждого элемента выведите число или "нет данных", используя ??.


B. switch и диапазоны

  1. Калькулятор через switch. Напишите программу, которая принимает два числа и оператор, затем вычисляет результат:
let x = 10.0
let y = 3.0
let op = "/"
let result: String
switch op {
case "+":
result = "\(x + y)"
case "-":
result = "\(x - y)"
case "*":
result = "\(x * y)"
case "/":
if y != 0 {
result = "\(x / y)"
} else {
result = "Ошибка: деление на ноль"
}
default:
result = "Неизвестный оператор: \(op)"
}
print("\(x) \(op) \(y) = \(result)")

Протестируйте для всех операторов (+, -, *, /), а также для деления на ноль и неизвестного оператора.

  1. Оценка через switch с диапазонами. Напишите функцию, которая по числовому баллу (0–100) возвращает текстовую оценку:
func grade(score: Int) -> String {
switch score {
case 90...100: return "Отлично (A)"
case 75..<90: return "Хорошо (B)"
case 60..<75: return "Удовлетворительно (C)"
case 0..<60: return "Неудовлетворительно (F)"
default: return "Некорректный балл"
}
}

Протестируйте для значений: 95, 82, 67, 45, 100, 0, -5, 110. Ожидаемый вывод:

95 → Отлично (A)
82 → Хорошо (B)
67 → Удовлетворительно (C)
45 → Неудовлетворительно (F)
100 → Отлично (A)
0 → Неудовлетворительно (F)
-5 → Некорректный балл
110 → Некорректный балл

C. guard и валидация

  1. Валидатор пароля. Напишите функцию validatePassword(_ password: String?) -> Bool, использующую guard для проверки:
    • Пароль не nil.
    • Длина >= 8 символов.
    • Содержит хотя бы одну цифру.
    • Содержит хотя бы одну заглавную букву.
func validatePassword(_ password: String?) -> Bool {
guard let password = password else {
print("Ошибка: пароль не задан")
return false
}
guard password.count >= 8 else {
print("Ошибка: длина менее 8 символов")
return false
}
guard password.contains(where: { $0.isNumber }) else {
print("Ошибка: нет цифры")
return false
}
guard password.contains(where: { $0.isUppercase }) else {
print("Ошибка: нет заглавной буквы")
return false
}
return true
}

Протестируйте для: nil, "abc", "abcdefgh", "abcdefg1", "Abcdefg1", "MyP@ss1". Выведите результат каждой проверки.

  1. Валидатор регистрации. Используя guard, напишите функцию validateRegistration(name:email:age:), которая проверяет:
    • Имя не пустое.
    • Email содержит "@" и ".".
    • Возраст от 16 до 120. Функция возвращает (valid: Bool, error: String?).
let checks = [
("Иван", "ivan@mail.ru", 20),
("", "test@mail.ru", 25),
("Анна", "anna-mail.ru", 22),
("Пётр", "petr@mail.ru", 15),
]
for (name, email, age) in checks {
let result = validateRegistration(name: name, email: email, age: age)
if result.valid {
print("\(name): регистрация успешна")
} else {
print("\(name): \(result.error!)")
}
}

D. Циклы

  1. FizzBuzz. Напишите классическую задачу FizzBuzz для чисел от 1 до 30:
    • Если число делится на 3 — выведите "Fizz".
    • Если число делится на 5 — выведите "Buzz".
    • Если делится на оба — выведите "FizzBuzz".
    • Иначе — выведите само число.

Реализуйте два варианта:

  • С использованием if/else if/else.
  • С использованием switch и кортежа (n % 3 == 0, n % 5 == 0):
for n in 1...30 {
switch (n % 3 == 0, n % 5 == 0) {
case (true, true): print("FizzBuzz")
case (true, false): print("Fizz")
case (false, true): print("Buzz")
default: print(n)
}
}
  1. Фибоначчи. Выведите первые 20 чисел Фибоначчи, используя цикл while:
var fib1 = 0
var fib2 = 1
var count = 0
while count < 20 {
print(fib1, terminator: " ")
let next = fib1 + fib2
fib1 = fib2
fib2 = next
count += 1
}
print()

Ожидаемый вывод:

0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181

Реализуйте второй вариант — через repeat-while. В комментарии объясните, в чём разница между while и repeat-while.


E. stride и labeled statements

  1. stride. Выведите:
    • Чётные числа от 2 до 50 (через stride(from:through:by:)).
    • Обратный отсчёт от 10 до 0 (через stride(from:through:by:) с отрицательным шагом).
    • Числа с шагом 0.5 от 0.0 до 5.0 (через stride(from:to:by:)).
// чётные от 2 до 50
for n in stride(from: 2, through: 50, by: 2) {
print(n, terminator: " ")
}
print()
// обратный отсчёт
for n in stride(from: 10, through: 0, by: -1) {
print(n, terminator: " ")
}
print()

В комментарии объясните разницу между stride(from:to:by:) и stride(from:through:by:).

  1. Поиск пары (labeled break). Найдите первую пару (i, j) из диапазона 1...20, где i * j == 200, используя labeled statements:
search: for i in 1...20 {
for j in 1...20 {
if i * j == 200 {
print("Найдена пара: (\(i), \(j))")
break search
}
}
}

Модифицируйте программу: найдите все пары (i, j) где i * j == 200 и i <= j (чтобы не дублировать). Используйте labeled continue для пропуска ненужных итераций.


F. Мини-проект — «Угадай число»

  1. Создайте консольную игру guess.swift:
  • Программа «загадывает» число от 1 до 100 (используйте Int.random(in: 1...100)).
  • Пользователь вводит число через readLine().
  • Программа отвечает: "Больше", "Меньше" или "Угадали за N попыток!".
  • Используйте guard для валидации ввода (не nil, является числом, в диапазоне 1–100).
  • Используйте repeat-while для основного цикла.
  • Ограничьте количество попыток (например, 10). При превышении — "Вы проиграли! Было загадано: \(secret)".

Примерный ход игры:

Я загадал число от 1 до 100. У вас 10 попыток.
Ваш ответ: 50
Больше!
Ваш ответ: 75
Меньше!
Ваш ответ: 63
Угадали за 3 попытки!

Подсказки:

  • readLine() возвращает String?.
  • Int(readLine() ?? "") — безопасное преобразование.
  • Используйте switch для сравнения введённого числа с загаданным.

G. Критерии оценивания

  • Корректность компиляции и работы всех программ на Linux: 0–4 балла
  • Правильное использование switch, guard, циклов, stride: 0–4 балла
  • Полнота выполнения заданий (все 11 пунктов): 0–4 балла
  • Оформление кода: комментарии с пояснениями, отсутствие дублирования, читаемость: 0–4 балла

Максимум: 16 баллов. Бонус до +2 за задания из раздела «Дополнительно».


H. Дополнительно (по желанию)

  • В игре «Угадай число» добавьте режим, где компьютер угадывает число пользователя методом бинарного поиска.
  • Напишите программу, выводящую «треугольник Паскаля» первых 10 строк, используя вложенные циклы.
  • Реализуйте switch с кортежем для простой системы координат: определите, в каком квадранте находится точка (x, y), используя switch (x > 0, y > 0).