Пример задачи на простые классы
Решим теперь с помощью классов 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 (этот файл не содержит нерешённых задач).
Переходите к следующему разделу.