Пример задачи на простые классы
Решим теперь с помощью классов Point
и Triangle
следующую задачу. Пусть имеется треугольник ABC, заданный координатами вершин, и точка P. Необходимо определить, лежит ли точка внутри треугольника.
data class Triangle(val a: Point, val b: Point, val c: Point) { fun halfPerimeter() = (a.distance(b) + b.distance(c) + c.distance(a)) / 2.0 fun area(): Double { val p = halfPerimeter() return sqrt(p * (p - a.distance(b)) * (p - b.distance(c)) * (p - c.distance(a))) } fun contains(p: Point): Boolean { val abp = Triangle(a, b, p) val bcp = Triangle(b, c, p) val cap = Triangle(c, a, p) return abp.area() + bcp.area() + cap.area() <= area() } }
Для решения задачи нам потребовалось определить три новых функции в классе Triangle
.
Пойдём от простого к сложному. Функция halfPerimeter()
считает полупериметр треугольника, то есть половину его периметра. Для этого мы считаем длину отрезков AB, BC и CA, суммируем эти длины и делим результат пополам. Длина отрезка AB (например) считается как a.distance(b)
— мы используем ранее определённую функцию точки distance
.
Функция area()
считает площадь треугольника, используя для этой цели формулу Герона: S2 = p(p - AB)(p - BC)(p - CA)
. Здесь S
— площадь, p
— полупериметр, AB
, BC
и CA
— длины сторон. Для расчёта полупериметра мы используем уже готовую функцию halfPerimeter()
.
Наконец, функция contains()
решает исходную задачу, то есть определяет, находится ли точка, заданная параметром p
, внутри треугольника-получателя. Для этой цели, кроме уже существующего треугольника-получателя ABC
, мы создаём три других: ABP
, BCP
, CAP
и считаем площади всех четырёх треугольников. Проверьте, что в случае присутствия точки P
внутри треугольника должно выполняться равенство: S(ABC) = S(ABP) + S(BCP) + S(CAP)
. Это становится очевидно, если нарисовать все эти треугольники.
Готовые классы с данными, деструктурирование
В Котлине имеются два готовых класса с данными, которые могут применяться в программе, если потребовалось объединить в один тип два или три связанных значения других типов. Это класс Pair<A, B>
(пара) со свойствами first
и second
типов A
и B
и класс Triple<A, B, C>
(тройка) со свойствами first
, second
и third
типов A
, B
и C
. Например:
fun combinePairs(pair1: Pair<String, Int>, pair2: Pair<Int, String>): Triple<String, Int, String> = Triple(pair1.first, pair1.second + pair2.first, pair2.second)
Такая функция комбинирует две пары в тройку, складывая второй элемент первой пары с первым элементом второй.
Пара и тройка полезны также в тех случаях, когда хочется получить от функции несколько результатов. Например:
fun timeStrToSeconds(str: String ): Triple<Int, Int, Int> { val parts = str.split(":").map { it.toInt() } return Triple(parts[0], parts[1], parts[2]) }
Данная функция преобразует строку вида «11:34:45» в тройку (часы, минуты, секунды). Она может быть использована так:
fun useTimeStrToSeconds() { val triple = timeStrToSeconds("11:34:45") val hh = triple.first val mm = triple.second val ss = triple.third // или: деструктурирование val (hours, minutes, seconds) = timeStrToSeconds("11:34:45") }
Деструктурирование в последней строчке функции позволяет выполнить разбиение тройки на отдельные компоненты, создавая три переменных hours
, minutes
, seconds
. Та же операция доступна для любого другого класса с данными (data class). Другой пример его использования:
fun test() { val list = listOf("abc", "def") for ((index, value) in list.withIndex()) { println("#$index: $value") } }
Функция list.withIndex()
возвращает список объектов типа IndexedValue
, содержащих индекс элемента списка и его значение. Класс IndexedValue
определён следующим образом:
data class IndexedValue<T>(val index: Int, val value: T)
Такая функция test
выведет на консоль строчки #0: abc
и #1: def
.
Упражнения
Откройте файл srс/lesson8/task1/Geometry.kt
в проекте KotlinAsFirst
.
Посмотрите на задачи в нём. Кроме уже рассмотренного класса Point
, в данном уроке используются классы Circle
(окружность), Segment
(отрезок), Line
(прямая). Попробуйте порешать задачи данного урока; рекомендуется делать это последовательно, от простого к сложному, с проверкой правильности решения каждой из задач с помощью тестов. Тесты, как и всегда, находятся в test/lesson8/task1/Tests.kt
Пройдите в этой группе задач так далеко, как сможете. Рекомендуется попробовать решить одну из двух очень сложных задач в конце файла. Если у вас возникают сложности с придумыванием алгоритма решения задачи, обсудите алгоритм с преподавателем.
Откройте теперь файл srс/lesson8/task1/Chess.kt
. Файл содержит задачи на поиск траектории движения различных шахматных фигур из клетки в клетку доски — короля, ладьи, слона, коня. Правила передвижения фигур описаны в комментариях к функциям.
В этом файле рекомендуется решить, по крайней мере, две задачи про одну из фигур (на определение длины траектории и самой траектории). Имейте в виду, что поиск траектории для коня достаточно сложен; прежде, чем приступать к этой задаче, рекомендуется ознакомиться с содержимым раздела 8.5 про поиск пути на графах и примерами в src/lesson8/task3/Graph.kt
(этот файл не содержит нерешённых задач).
Переходите к следующему разделу.