Пример задачи на простые классы

Решим теперь с помощью классов 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 — полупериметр, ABBC и CA — длины сторон. Для расчёта полупериметра мы используем уже готовую функцию halfPerimeter().

Наконец, функция contains() решает исходную задачу, то есть определяет, находится ли точка, заданная параметром p, внутри треугольника-получателя. Для этой цели, кроме уже существующего треугольника-получателя ABC, мы создаём три других: ABPBCPCAP и считаем площади всех четырёх треугольников. Проверьте, что в случае присутствия точки P внутри треугольника должно выполняться равенство: S(ABC) = S(ABP) + S(BCP) + S(CAP). Это становится очевидно, если нарисовать все эти треугольники.

Готовые классы с данными, деструктурирование

В Котлине имеются два готовых класса с данными, которые могут применяться в программе, если потребовалось объединить в один тип два или три связанных значения других типов. Это класс Pair<A, B> (пара) со свойствами first и second типов A и B и класс Triple<A, B, C> (тройка) со свойствами firstsecond и third типов AB и 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")
}

Деструктурирование в последней строчке функции позволяет выполнить разбиение тройки на отдельные компоненты, создавая три переменных hoursminutesseconds. Та же операция доступна для любого другого класса с данными (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 (этот файл не содержит нерешённых задач).

Переходите к следующему разделу.

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.