Практика 7. Практика к лекции 7
Цель: закрепить работу со структурами и классами в Swift — разницу между типами-значениями и ссылочными типами, хранимые и вычисляемые свойства, наблюдатели свойств (willSet/didSet), мутирующие методы, инициализаторы и статические свойства — через серию упражнений от базовых к проектным.
Рекомендации по выполнению:
- Выполняйте задания в отдельных файлах
.swift(например,task1.swift,task2.swift). - Запускайте через терминал:
swift task1.swift(илиswift имя_файла.swift). - Для проектов из нескольких файлов используйте Swift Package Manager (
swift package init --type executable). - Работа ведётся на Linux — Xcode не требуется, достаточно установленного Swift toolchain.
- Для каждого задания добавляйте проверочные
print/assert-выражения, демонстрирующие работу.
A. Разминка
- Создайте структуру
Temperatureс хранимым свойствомcelsius: Doubleи вычисляемым свойствомfahrenheit(сgetиset). Формула:F = C × 9/5 + 32.
var temp = Temperature(celsius: 100)print(temp.fahrenheit) // 212.0temp.fahrenheit = 32print(temp.celsius) // 0.0- Создайте структуру
Vector2Dсо свойствамиx: Double,y: Double. Добавьте:- Вычисляемое свойство
magnitude(длина вектора:sqrt(x*x + y*y)). - Мутирующий метод
mutating func scale(by factor: Double)— умножаетxиyнаfactor. - Статический метод
static func add(_ a: Vector2D, _ b: Vector2D) -> Vector2D.
- Вычисляемое свойство
var v1 = Vector2D(x: 3, y: 4)print(v1.magnitude) // 5.0v1.scale(by: 2)print(v1) // Vector2D(x: 6.0, y: 8.0)
let v2 = Vector2D(x: 1, y: 1)let v3 = Vector2D.add(v1, v2)print(v3) // Vector2D(x: 7.0, y: 9.0)B. Свойства и наблюдатели
- Создайте структуру
Circleс хранимым свойствомradius: Doubleи двумя вычисляемыми свойствами:area: Double(только для чтения) — площадь круга (Double.pi * radius * radius).diameter: Double(сgetиset) — при установкеdiameterпересчитываетсяradius.
var c = Circle(radius: 5)print(c.area) // ≈78.54print(c.diameter) // 10.0c.diameter = 6print(c.radius) // 3.0- Создайте структуру
StepCounterсо свойствомtotalSteps: Intс наблюдателемdidSet, который выводит количество добавленных шагов и текущее общее количество.
var stepper = StepCounter(totalSteps: 0)stepper.totalSteps = 100 // Шагов добавлено: 100. Всего: 100.stepper.totalSteps = 360 // Шагов добавлено: 260. Всего: 360.Дополнительно: добавьте willSet, который выводит "Значение изменится с X на Y".
C. Типы-значения vs ссылочные типы
- Создайте структуру
PointStructи классPointClass, оба сvar x: Int, y: Int. Выполните следующий эксперимент:
var structA = PointStruct(x: 1, y: 2)var structB = structAstructB.x = 99print(structA.x) // Что выведет?print(structB.x) // Что выведет?
let classA = PointClass(x: 1, y: 2)let classB = classAclassB.x = 99print(classA.x) // Что выведет?print(classB.x) // Что выведет?print(classA === classB) // Что выведет?Объясните разницу в поведении комментарием в коде. Почему classA.x изменился, хотя мы меняли classB?
- Напишите функцию
modifyPoint, принимающуюPointClassи увеличивающуюxна 10. Вызовите её и убедитесь, что оригинальный объект изменился. Затем напишите аналогичную функцию дляPointStructс ключевым словомinoutи сравните поведение.
func modifyClass(_ p: PointClass) { p.x += 10}
func modifyStruct(_ p: inout PointStruct) { p.x += 10}
let obj = PointClass(x: 5, y: 5)modifyClass(obj)print(obj.x) // 15
var val = PointStruct(x: 5, y: 5)modifyStruct(&val)print(val.x) // 15D. Методы и инициализаторы
-
Создайте структуру
BankCardсо свойствами:let number: String— номер карты.let holder: String— имя владельца.private(set) var balance: Double— баланс (изменяемый только внутри структуры).
Добавьте методы:
mutating func deposit(_ amount: Double)— пополнение (с проверкойamount > 0).mutating func withdraw(_ amount: Double) -> Bool— снятие (возвращаетfalse, если недостаточно средств).- Кастомный инициализатор, принимающий
number,holderи необязательныйinitialBalance(по умолчанию0).
var card = BankCard(number: "4276-XXXX-XXXX-1234", holder: "Иван Петров", initialBalance: 1000)card.deposit(500)print(card.balance) // 1500.0print(card.withdraw(2000)) // falseprint(card.withdraw(300)) // trueprint(card.balance) // 1200.0- Создайте структуру
Counterсо:- Статическим свойством
static var totalCreated: Int = 0— общее количество созданных экземпляров. - Хранимым свойством
var count: Int = 0. - Методом
mutating func increment(by amount: Int = 1). - Инициализатором, увеличивающим
Counter.totalCreated.
- Статическим свойством
var c1 = Counter()var c2 = Counter()c1.increment(by: 5)c2.increment()print(c1.count) // 5print(c2.count) // 1print(Counter.totalCreated) // 2E. Связный список (class)
- Создайте класс
LinkedNodeсvar value: Intиvar next: LinkedNode?. Постройте цепочку из 4–5 узлов и реализуйте:- Функцию
printList(_ head: LinkedNode?)— выводит значения в формате1 -> 2 -> 3 -> nil. - Функцию
sum(_ head: LinkedNode?) -> Int— возвращает сумму всех значений. - Функцию
count(_ head: LinkedNode?) -> Int— возвращает количество узлов.
- Функцию
Объясните в комментарии, почему для связного списка необходим class, а не struct.
let node3 = LinkedNode(value: 30, next: nil)let node2 = LinkedNode(value: 20, next: node3)let node1 = LinkedNode(value: 10, next: node2)
printList(node1) // 10 -> 20 -> 30 -> nilprint(sum(node1)) // 60print(count(node1)) // 3F. Мини-проект
- «Библиотека геометрических фигур» — реализуйте систему работы с фигурами, используя структуры:
struct Point { var x: Double var y: Double}
struct Size { var width: Double var height: Double}
struct Rectangle { var origin: Point var size: Size
var area: Double { ... } // вычисляемое свойство var perimeter: Double { ... } // вычисляемое свойство var center: Point { ... } // вычисляемое свойство (get и set)
func contains(_ point: Point) -> Bool { ... } mutating func translate(dx: Double, dy: Double) { ... }}Реализуйте:
- Все вычисляемые свойства (
area,perimeter,center). - Установку
centerдолжна пересчитыватьorigin. - Метод
contains— проверяет, лежит ли точка внутри прямоугольника. - Метод
translate— сдвигает прямоугольник на(dx, dy).
Создайте массив из нескольких Rectangle и используя функциональные методы:
- Найдите прямоугольник с наибольшей площадью.
- Отфильтруйте прямоугольники, содержащие точку
(5, 5).
var rect = Rectangle(origin: Point(x: 0, y: 0), size: Size(width: 10, height: 5))print(rect.area) // 50.0print(rect.center) // Point(x: 5.0, y: 2.5)rect.center = Point(x: 0, y: 0)print(rect.origin) // Point(x: -5.0, y: -2.5)print(rect.contains(Point(x: -3, y: -1))) // trueКритерии оценивания
- Корректность и полнота реализации: 0–5 баллов
- Правильное использование
structиclass, понимание value/reference семантики: 0–5 баллов - Качество и читаемость кода (именование, вычисляемые свойства,
mutating,private(set)): 0–3 балла - Наличие проверочных
print/assertвыражений: 0–3 балла
Максимум: 16 баллов. Бонус до +2 за дополнительные задания.
Дополнительно (по желанию)
- Добавьте к
Vector2Dреализацию протоколаCustomStringConvertibleдля красивого вывода (аналог__repr__в Python). - Реализуйте структуру
Stack<Element>с методамиpush,pop,peekи вычисляемым свойствомisEmpty. - Добавьте в мини-проект структуру
Circleс аналогичным интерфейсом и функцию, которая определяет, пересекаются ли два прямоугольника.