", data.convertToHtmlTableUsingKotlinxHtml().replace("\\s+".toRegex(), ""))
27 | }
28 |
29 | @Test
30 | @Tag("Example")
31 | fun generateSimpleHtml() {
32 | assertEquals("Hello!", generateSimpleHtml("Hello!"))
33 | }
34 | }
--------------------------------------------------------------------------------
/input/align_in1.txt:
--------------------------------------------------------------------------------
1 | Для написания разных видов программ сейчас применяются разные языки программирования.
2 | Например, в сфере мобильных программ сейчас правят бал языки Swift (мобильные устройства под управлением iOS)
3 | и Java (устройства под управлением Android).
4 | Системные программы, как правило, пишутся на языках C или {cpp}.
5 | Эти же языки долгое время использовались и для создания встраиваемых программ,
6 | но в последние годы в этой области набирает популярность язык Java.
7 | Для написания web-клиентов часто используется JavaScript, а в простых случаях -- язык разметки страниц HTML.
8 | Web-серверы используют опять-таки Java (в сложных случаях), а также Python и PHP (в более простых).
9 | Наконец, простые desktop-программы сейчас могут быть написаны на самых разных языках,
10 | и выбор во многом зависит от сложности программы, области её использования, предполагаемой операционной системы.
11 | В первую очередь следует назвать языки Java, {cpp}, C#, Python, Visual Basic, Ruby, Swift.
12 |
13 | Самым универсальным и одновременно самым распространённым языком программирования
14 | на данный момент следует считать язык Java.
15 | Java в широком смысле -- не только язык, но и платформа для выполнения программ
16 | под самыми разными операционными системами и на разной аппаратуре.
17 | Такая универсальность обеспечивается наличием виртуальной машины Java --
18 | системной программы, интерпретирующей Java байт-код в машинные коды конкретного компьютера или системы.
19 | Java также включает богатейший набор библиотек для разработки.
20 |
--------------------------------------------------------------------------------
/test/lesson9/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson9.task1
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 |
7 | class Tests {
8 | @Test
9 | @Tag("Easy")
10 | fun createMatrix() {
11 | val matrix = createMatrix(4, 6, 0.0)
12 | assertEquals(4, matrix.height)
13 | assertEquals(6, matrix.width)
14 | }
15 |
16 | @Test
17 | @Tag("Normal")
18 | fun getSetInt() {
19 | val matrix = createMatrix(3, 2, 0)
20 | var value = 0
21 | for (row in 0 until matrix.height) {
22 | for (column in 0 until matrix.width) {
23 | matrix[row, column] = value++
24 | }
25 | }
26 | value = 0
27 | for (row in 0 until matrix.height) {
28 | for (column in 0 until matrix.width) {
29 | assertEquals(value++, matrix[Cell(row, column)])
30 | }
31 | }
32 | }
33 |
34 | @Test
35 | @Tag("Normal")
36 | fun getSetString() {
37 | val matrix = createMatrix(2, 2, "")
38 | val strings = listOf("alpha", "beta", "gamma", "omega")
39 | var index = 0
40 | for (row in 0 until matrix.height) {
41 | for (column in 0 until matrix.width) {
42 | matrix[Cell(row, column)] = strings[index++]
43 | }
44 | }
45 | index = 0
46 | for (row in 0 until matrix.height) {
47 | for (column in 0 until matrix.width) {
48 | assertEquals(strings[index++], matrix[row, column])
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/lesson9/task1/Matrix.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER", "unused")
2 | package lesson9.task1
3 |
4 | /**
5 | * Ячейка матрицы: row = ряд, column = колонка
6 | */
7 | data class Cell(val row: Int, val column: Int)
8 |
9 | /**
10 | * Интерфейс, описывающий возможности матрицы. E = тип элемента матрицы
11 | */
12 | interface Matrix {
13 | /** Высота */
14 | val height: Int
15 |
16 | /** Ширина */
17 | val width: Int
18 |
19 | /**
20 | * Доступ к ячейке.
21 | * Методы могут бросить исключение, если ячейка не существует или пуста
22 | */
23 | operator fun get(row: Int, column: Int): E
24 | operator fun get(cell: Cell): E
25 |
26 | /**
27 | * Запись в ячейку.
28 | * Методы могут бросить исключение, если ячейка не существует
29 | */
30 | operator fun set(row: Int, column: Int, value: E)
31 | operator fun set(cell: Cell, value: E)
32 | }
33 |
34 | /**
35 | * Простая
36 | *
37 | * Метод для создания матрицы, должен вернуть РЕАЛИЗАЦИЮ Matrix.
38 | * height = высота, width = ширина, e = чем заполнить элементы.
39 | * Бросить исключение IllegalArgumentException, если height или width <= 0.
40 | */
41 | fun createMatrix(height: Int, width: Int, e: E): Matrix = TODO()
42 |
43 | /**
44 | * Средняя сложность
45 | *
46 | * Реализация интерфейса "матрица"
47 | */
48 | class MatrixImpl : Matrix {
49 | override val height: Int = TODO()
50 |
51 | override val width: Int = TODO()
52 |
53 | override fun get(row: Int, column: Int): E = TODO()
54 |
55 | override fun get(cell: Cell): E = TODO()
56 |
57 | override fun set(row: Int, column: Int, value: E) {
58 | TODO()
59 | }
60 |
61 | override fun set(cell: Cell, value: E) {
62 | TODO()
63 | }
64 |
65 | override fun equals(other: Any?) = TODO()
66 |
67 | override fun toString(): String = TODO()
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/src/lesson2/task2/Logical.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson2.task2
3 |
4 | import lesson1.task1.sqr
5 |
6 | /**
7 | * Пример
8 | *
9 | * Лежит ли точка (x, y) внутри окружности с центром в (x0, y0) и радиусом r?
10 | */
11 | fun pointInsideCircle(x: Double, y: Double, x0: Double, y0: Double, r: Double) =
12 | sqr(x - x0) + sqr(y - y0) <= sqr(r)
13 |
14 | /**
15 | * Простая
16 | *
17 | * Четырехзначное число назовем счастливым, если сумма первых двух ее цифр равна сумме двух последних.
18 | * Определить, счастливое ли заданное число, вернуть true, если это так.
19 | */
20 | fun isNumberHappy(number: Int): Boolean = TODO()
21 |
22 | /**
23 | * Простая
24 | *
25 | * На шахматной доске стоят два ферзя (ферзь бьет по вертикали, горизонтали и диагоналям).
26 | * Определить, угрожают ли они друг другу. Вернуть true, если угрожают.
27 | * Считать, что ферзи не могут загораживать друг друга.
28 | */
29 | fun queenThreatens(x1: Int, y1: Int, x2: Int, y2: Int): Boolean = TODO()
30 |
31 |
32 | /**
33 | * Простая
34 | *
35 | * Дан номер месяца (от 1 до 12 включительно) и год (положительный).
36 | * Вернуть число дней в этом месяце этого года по григорианскому календарю.
37 | */
38 | fun daysInMonth(month: Int, year: Int): Int = TODO()
39 |
40 | /**
41 | * Средняя
42 | *
43 | * Проверить, лежит ли окружность с центром в (x1, y1) и радиусом r1 целиком внутри
44 | * окружности с центром в (x2, y2) и радиусом r2.
45 | * Вернуть true, если утверждение верно
46 | */
47 | fun circleInside(x1: Double, y1: Double, r1: Double,
48 | x2: Double, y2: Double, r2: Double): Boolean = TODO()
49 |
50 | /**
51 | * Средняя
52 | *
53 | * Определить, пройдет ли кирпич со сторонами а, b, c сквозь прямоугольное отверстие в стене со сторонами r и s.
54 | * Стороны отверстия должны быть параллельны граням кирпича.
55 | * Считать, что совпадения длин сторон достаточно для прохождения кирпича, т.е., например,
56 | * кирпич 4 х 4 х 4 пройдёт через отверстие 4 х 4.
57 | * Вернуть true, если кирпич пройдёт
58 | */
59 | fun brickPasses(a: Int, b: Int, c: Int, r: Int, s: Int): Boolean = TODO()
60 |
--------------------------------------------------------------------------------
/src/lesson10/task2/Html.kt:
--------------------------------------------------------------------------------
1 | package lesson10.task2
2 |
3 | import kotlinx.html.*
4 | import kotlinx.html.stream.appendHTML
5 |
6 | /**
7 | * Пример: преобразование списка списков в HTML таблицу
8 | */
9 | fun List>.convertToHtmlTable(): String {
10 | val sb = StringBuilder()
11 | sb.append("")
12 | sb.append("")
13 | sb.append("
")
14 | for (row in this) {
15 | sb.append("
")
16 | for (data in row) {
17 | sb.append("
")
18 | sb.append(data)
19 | sb.append("
")
20 | }
21 | sb.append("
")
22 | }
23 | sb.append("
")
24 | sb.append("")
25 | sb.append("")
26 | return sb.toString()
27 | }
28 |
29 | fun List>.convertToHtmlTableUsingKotlinxHtml(): String {
30 | val inputList = this
31 | val sb = StringBuilder()
32 | sb.appendHTML().html {
33 | body {
34 | table {
35 | for (row in inputList) {
36 | tr {
37 | for (data in row) {
38 | td { +data }
39 | }
40 | }
41 | }
42 | }
43 | }
44 | }
45 | return sb.toString()
46 | }
47 |
48 | fun generateSimpleHtml(s: String): String {
49 | val sb = StringBuilder()
50 | sb.myHtml {
51 | myBody {
52 | +s
53 | }
54 | }
55 | return sb.toString()
56 | }
57 |
58 | private class HTML(val sb: StringBuilder) {
59 | fun myBody(init: HTMLBody.() -> Unit): HTMLBody {
60 | val body = HTMLBody(sb)
61 | sb.append("")
62 | body.init()
63 | sb.append("")
64 | return body
65 | }
66 | }
67 |
68 | private class HTMLBody(val sb: StringBuilder) {
69 | operator fun String.unaryPlus() {
70 | sb.append(this)
71 | }
72 | }
73 |
74 | private fun StringBuilder.myHtml(init: HTML.() -> Unit): HTML {
75 | val html = HTML(this)
76 | append("")
77 | html.init()
78 | append("")
79 | return html
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/test/lesson8/task3/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson8.task3
2 |
3 | import org.junit.jupiter.api.Tag
4 | import org.junit.jupiter.api.Test
5 | import org.junit.jupiter.api.Assertions.assertEquals
6 |
7 | class Tests {
8 |
9 | @Test
10 | @Tag("Example")
11 | fun bfs() {
12 | val graph = Graph()
13 | graph.addVertex("A")
14 | graph.addVertex("B")
15 | graph.addVertex("C")
16 | graph.addVertex("D")
17 | graph.addVertex("E")
18 | graph.addVertex("F")
19 | graph.addVertex("G")
20 | graph.addVertex("H")
21 | graph.connect("A", "B")
22 | graph.connect("B", "C")
23 | graph.connect("B", "D")
24 | graph.connect("C", "E")
25 | graph.connect("D", "F")
26 | graph.connect("C", "F")
27 | graph.connect("G", "H")
28 | assertEquals(0, graph.bfs("A", "A"))
29 | assertEquals(3, graph.bfs("A", "F"))
30 | assertEquals(2, graph.bfs("E", "F"))
31 | assertEquals(3, graph.bfs("E", "D"))
32 | assertEquals(1, graph.bfs("H", "G"))
33 | assertEquals(-1, graph.bfs("H", "A"))
34 | }
35 |
36 |
37 | @Test
38 | @Tag("Example")
39 | fun dfs() {
40 | val graph = Graph()
41 | graph.addVertex("A")
42 | graph.addVertex("B")
43 | graph.addVertex("C")
44 | graph.addVertex("D")
45 | graph.addVertex("E")
46 | graph.addVertex("F")
47 | graph.addVertex("G")
48 | graph.addVertex("H")
49 | graph.connect("A", "B")
50 | graph.connect("B", "C")
51 | graph.connect("B", "D")
52 | graph.connect("C", "E")
53 | graph.connect("D", "F")
54 | graph.connect("C", "F")
55 | graph.connect("G", "H")
56 | assertEquals(0, graph.dfs("A", "A"))
57 | assertEquals(1, graph.dfs("A", "B"))
58 | assertEquals(2, graph.dfs("A", "C"))
59 | assertEquals(2, graph.dfs("B", "F"))
60 | assertEquals(3, graph.dfs("A", "F"))
61 | assertEquals(2, graph.dfs("E", "F"))
62 | assertEquals(3, graph.dfs("E", "D"))
63 | assertEquals(1, graph.dfs("H", "G"))
64 | assertEquals(-1, graph.dfs("H", "A"))
65 | }
66 | }
--------------------------------------------------------------------------------
/test/lesson2/task2/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson2.task2
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 |
7 | class Tests {
8 | @Test
9 | @Tag("Example")
10 | fun pointInsideCircle() {
11 | // (1, 1) inside circle: center = (0, 0), r = 2
12 | assertTrue(pointInsideCircle(1.0, 1.0, 0.0, 0.0, 2.0))
13 | // (2, 2) NOT inside circle: center = (0, 0), r = 2
14 | assertFalse(pointInsideCircle(2.0, 2.0, 0.0, 0.0, 2.0))
15 | }
16 |
17 | @Test
18 | @Tag("Easy")
19 | fun isNumberHappy() {
20 | assertTrue(isNumberHappy(1533))
21 | assertTrue(isNumberHappy(9009))
22 | assertFalse(isNumberHappy(3644))
23 | }
24 |
25 | @Test
26 | @Tag("Easy")
27 | fun queenThreatens() {
28 | assertTrue(queenThreatens(3, 6, 7, 6))
29 | assertTrue(queenThreatens(8, 1, 1, 8))
30 | assertFalse(queenThreatens(7, 6, 5, 7))
31 | }
32 |
33 | @Test
34 | @Tag("Easy")
35 | fun daysInMonth() {
36 | assertEquals(31, daysInMonth(1, 1990))
37 | assertEquals(28, daysInMonth(2, 1990))
38 | assertEquals(31, daysInMonth(3, 1990))
39 | assertEquals(30, daysInMonth(4, 1990))
40 | assertEquals(31, daysInMonth(5, 1990))
41 | assertEquals(30, daysInMonth(6, 1990))
42 | assertEquals(31, daysInMonth(7, 1990))
43 | assertEquals(31, daysInMonth(8, 1990))
44 | assertEquals(29, daysInMonth(2, 1992))
45 | assertEquals(29, daysInMonth(2, 1996))
46 | assertEquals(28, daysInMonth(2, 1900))
47 | assertEquals(29, daysInMonth(2, 2000))
48 | }
49 |
50 | @Test
51 | @Tag("Normal")
52 | fun circleInside() {
53 | assertFalse(circleInside(0.0, 0.0, 6.0, 0.0, 0.0, 5.0))
54 | assertFalse(circleInside(0.0, 0.0, 1.0, 10.0, 10.0, 9.0))
55 | assertTrue(circleInside(2.0, 2.0, 2.0, 2.0, 2.0, 2.0))
56 | assertTrue(circleInside(-2.0, 3.0, 2.0, -2.0, 0.0, 5.0))
57 | assertFalse(circleInside(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
58 | }
59 |
60 | @Test
61 | @Tag("Normal")
62 | fun brickPasses() {
63 | assertTrue(brickPasses(2, 10, 5, 6, 3))
64 | assertTrue(brickPasses(4, 4, 4, 4, 4))
65 | assertFalse(brickPasses(6, 5, 4, 3, 6))
66 | assertTrue(brickPasses(3, 2, 1, 1, 2))
67 | }
68 | }
--------------------------------------------------------------------------------
/src/lesson8/task3/Graph.kt:
--------------------------------------------------------------------------------
1 | package lesson8.task3
2 |
3 | import java.util.*
4 |
5 | class Graph {
6 | private data class Vertex(val name: String) {
7 | val neighbors = mutableSetOf()
8 | }
9 |
10 | private val vertices = mutableMapOf()
11 |
12 | private operator fun get(name: String) = vertices[name] ?: throw IllegalArgumentException()
13 |
14 | fun addVertex(name: String) {
15 | vertices[name] = Vertex(name)
16 | }
17 |
18 | private fun connect(first: Vertex, second: Vertex) {
19 | first.neighbors.add(second)
20 | second.neighbors.add(first)
21 | }
22 |
23 | fun connect(first: String, second: String) = connect(this[first], this[second])
24 |
25 | /**
26 | * Пример
27 | *
28 | * По двум вершинам рассчитать расстояние между ними = число дуг на самом коротком пути между ними.
29 | * Вернуть -1, если пути между вершинами не существует.
30 | *
31 | * Используется поиск в ширину
32 | */
33 | fun bfs(start: String, finish: String) = bfs(this[start], this[finish])
34 |
35 | private fun bfs(start: Vertex, finish: Vertex): Int {
36 | val queue = ArrayDeque()
37 | queue.add(start)
38 | val visited = mutableMapOf(start to 0)
39 | while (queue.isNotEmpty()) {
40 | val next = queue.poll()
41 | val distance = visited[next]!!
42 | if (next == finish) return distance
43 | for (neighbor in next.neighbors) {
44 | if (neighbor in visited) continue
45 | visited.put(neighbor, distance + 1)
46 | queue.add(neighbor)
47 | }
48 | }
49 | return -1
50 | }
51 |
52 | /**
53 | * Пример
54 | *
55 | * По двум вершинам рассчитать расстояние между ними = число дуг на самом коротком пути между ними.
56 | * Вернуть -1, если пути между вершинами не существует.
57 | *
58 | * Используется поиск в глубину
59 | */
60 | fun dfs(start: String, finish: String): Int = dfs(this[start], this[finish], setOf()) ?: -1
61 |
62 | private fun dfs(start: Vertex, finish: Vertex, visited: Set): Int? =
63 | if (start == finish) 0
64 | else {
65 | val min = start.neighbors
66 | .filter { it !in visited }
67 | .mapNotNull { dfs(it, finish, visited + start) }
68 | .min()
69 | if (min == null) null else min + 1
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/lesson2/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson2.task1
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 |
7 | class Tests {
8 | @Test
9 | @Tag("Example")
10 | fun quadraticRootNumber() {
11 | assertEquals(1, quadraticRootNumber(1.0, 2.0, 1.0))
12 | assertEquals(2, quadraticRootNumber(1.0, 3.0, 2.0))
13 | assertEquals(0, quadraticRootNumber(1.0, 4.0, 10.0))
14 | }
15 |
16 | @Test
17 | @Tag("Example")
18 | fun gradeNotation() {
19 | assertEquals("отлично", gradeNotation(5))
20 | assertEquals("удовлетворительно", gradeNotation(3))
21 | }
22 |
23 | @Test
24 | @Tag("Example")
25 | fun minBiRoot() {
26 | assertEquals(Double.NaN, minBiRoot(0.0, 0.0, 1.0), 1e-2)
27 | assertEquals(Double.NaN, minBiRoot(0.0, 1.0, 2.0), 1e-2)
28 | assertEquals(-2.0, minBiRoot(0.0, 1.0, -4.0), 1e-10)
29 | assertEquals(Double.NaN, minBiRoot(1.0, -2.0, 4.0), 1e-2)
30 | assertEquals(Double.NaN, minBiRoot(1.0, 3.0, 2.0), 1e-2)
31 | assertEquals(-1.41, minBiRoot(1.0, -3.0, 2.0), 1e-2)
32 | }
33 |
34 | @Test
35 | @Tag("Easy")
36 | fun ageDescription() {
37 | assertEquals("1 год", ageDescription(1))
38 | assertEquals("21 год", ageDescription(21))
39 | assertEquals("132 года", ageDescription(132))
40 | assertEquals("12 лет", ageDescription(12))
41 | assertEquals("111 лет", ageDescription(111))
42 | assertEquals("199 лет", ageDescription(199))
43 | }
44 |
45 | @Test
46 | @Tag("Easy")
47 | fun timeForHalfWay() {
48 | assertEquals(2.5, timeForHalfWay(1.0, 5.0, 2.0, 4.0, 3.0, 3.0), 1e-2)
49 | assertEquals(3.67, timeForHalfWay(4.0, 3.0, 1.0, 4.0, 1.0, 6.0), 1e-2)
50 | assertEquals(4.4, timeForHalfWay(3.0, 0.0, 1.0, 6.0, 2.0, 5.0), 1e-2)
51 | }
52 |
53 | @Test
54 | @Tag("Easy")
55 | fun whichRookThreatens() {
56 | assertEquals(0, whichRookThreatens(1, 2, 3, 4, 5, 6))
57 | assertEquals(1, whichRookThreatens(5, 3, 7, 3, 4, 8))
58 | assertEquals(2, whichRookThreatens(6, 8, 8, 6, 6, 3))
59 | assertEquals(3, whichRookThreatens(3, 7, 8, 7, 3, 5))
60 | }
61 |
62 | @Test
63 | @Tag("Easy")
64 | fun rookOrBishopThreatens() {
65 | assertEquals(0, rookOrBishopThreatens(4, 5, 5, 7, 8, 8))
66 | assertEquals(1, rookOrBishopThreatens(2, 8, 6, 8, 1, 6))
67 | assertEquals(2, rookOrBishopThreatens(5, 4, 3, 7, 1, 8))
68 | assertEquals(3, rookOrBishopThreatens(1, 6, 7, 6, 3, 8))
69 | }
70 |
71 | @Test
72 | @Tag("Easy")
73 | fun triangleKind() {
74 | assertEquals(-1, triangleKind(3.0, 7.5, 4.0))
75 | assertEquals(1, triangleKind(5.0, 3.0, 4.0))
76 | assertEquals(2, triangleKind(4.0, 6.0, 8.0))
77 | assertEquals(0, triangleKind(1.0, 1.5, 1.5))
78 | }
79 |
80 | @Test
81 | @Tag("Normal")
82 | fun segmentLength() {
83 | assertEquals(-1, segmentLength(1, 2, 3, 4))
84 | assertEquals(-1, segmentLength(5, 7, 1, 3))
85 | assertEquals(0, segmentLength(1, 2, 2, 4))
86 | assertEquals(3, segmentLength(3, 6, 0, 9))
87 | assertEquals(2, segmentLength(2, 5, 3, 9))
88 | assertEquals(1, segmentLength(3, 6, 1, 4))
89 | assertEquals(4, segmentLength(1, 15, 10, 14))
90 | }
91 | }
--------------------------------------------------------------------------------
/test/lesson1/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson1.task1
2 |
3 | import org.junit.jupiter.api.Assertions.assertEquals
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 | import kotlin.math.PI
7 |
8 | class Tests {
9 | @Test
10 | @Tag("Example")
11 | fun sqr() {
12 | assertEquals(0, sqr(0))
13 | assertEquals(4, sqr(2))
14 | assertEquals(9, sqr(-3))
15 | }
16 |
17 | @Test
18 | @Tag("Example")
19 | fun sqrDouble() {
20 | assertEquals(0.0, sqr(0.0), 1e-13)
21 | assertEquals(4.0, sqr(2.0), 1e-13)
22 | assertEquals(9.0, sqr(-3.0), 1e-13)
23 | }
24 |
25 | @Test
26 | @Tag("Example")
27 | fun discriminant() {
28 | assertEquals(0.0, discriminant(0.0, 0.0, 0.0), 1e-13)
29 | assertEquals(0.0, discriminant(1.0, -2.0, 1.0), 1e-13)
30 | assertEquals(1.0, discriminant(1.0, 3.0, 2.0), 1e-13)
31 | }
32 |
33 | @Test
34 | @Tag("Example")
35 | fun quadraticEquationRoot() {
36 | assertEquals(2.0, quadraticEquationRoot(1.0, -3.0, 2.0), 1e-13)
37 | assertEquals(1.0, quadraticEquationRoot(1.0, -2.0, 1.0), 1e-13)
38 | assertEquals(-3.0, quadraticEquationRoot(1.0, 6.0, 9.0), 1e-13)
39 | }
40 |
41 | @Test
42 | @Tag("Example")
43 | fun quadraticRootProduct() {
44 | assertEquals(1.0, quadraticRootProduct(1.0, -2.0, 1.0), 1e-13)
45 | assertEquals(9.0, quadraticRootProduct(1.0, 6.0, 9.0), 1e-13)
46 | assertEquals(2.0, quadraticRootProduct(1.0, 3.0, 2.0), 1e-13)
47 | }
48 |
49 | @Test
50 | @Tag("Trivial")
51 | fun seconds() {
52 | assertEquals(30035, seconds(8, 20, 35))
53 | assertEquals(86400, seconds(24, 0, 0))
54 | assertEquals(13, seconds(0, 0, 13))
55 | }
56 |
57 | @Test
58 | @Tag("Trivial")
59 | fun lengthInMeters() {
60 | assertEquals(18.98, lengthInMeters(8, 2, 11), 1e-2)
61 | assertEquals(2.13, lengthInMeters(1, 0, 0), 1e-2)
62 | }
63 |
64 | @Test
65 | @Tag("Trivial")
66 | fun angleInRadian() {
67 | assertEquals(0.63256, angleInRadian(36, 14, 35), 1e-5)
68 | assertEquals(PI / 2.0, angleInRadian(90, 0, 0), 1e-5)
69 | }
70 |
71 | @Test
72 | @Tag("Trivial")
73 | fun trackLength() {
74 | assertEquals(5.0, trackLength(3.0, 0.0, 0.0, 4.0), 1e-5)
75 | assertEquals(1.0, trackLength(0.0, 1.0, -1.0, 1.0), 1e-5)
76 | assertEquals(1.41, trackLength(1.0, 1.0, 2.0, 2.0), 1e-2)
77 | }
78 |
79 | @Test
80 | @Tag("Easy")
81 | fun thirdDigit() {
82 | assertEquals(8, thirdDigit(3801))
83 | assertEquals(1, thirdDigit(100))
84 | assertEquals(0, thirdDigit(1000))
85 | }
86 |
87 | @Test
88 | @Tag("Easy")
89 | fun travelMinutes() {
90 | assertEquals(216, travelMinutes(9, 25, 13, 1))
91 | assertEquals(1, travelMinutes(21, 59, 22, 0))
92 | }
93 |
94 | @Test
95 | @Tag("Easy")
96 | fun accountInThreeYears() {
97 | assertEquals(133.1, accountInThreeYears(100, 10), 1e-2)
98 | assertEquals(1.0, accountInThreeYears(1, 0), 1e-2)
99 | assertEquals(104.0, accountInThreeYears(13, 100), 1e-2)
100 | }
101 |
102 | @Test
103 | @Tag("Easy")
104 | fun numberRevert() {
105 | assertEquals(874, numberRevert(478))
106 | assertEquals(201, numberRevert(102))
107 | }
108 | }
--------------------------------------------------------------------------------
/src/lesson1/task1/Simple.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson1.task1
3 |
4 | import kotlin.math.*
5 |
6 | /**
7 | * Пример
8 | *
9 | * Вычисление квадрата целого числа
10 | */
11 | fun sqr(x: Int) = x * x
12 |
13 | /**
14 | * Пример
15 | *
16 | * Вычисление квадрата вещественного числа
17 | */
18 | fun sqr(x: Double) = x * x
19 |
20 | /**
21 | * Пример
22 | *
23 | * Вычисление дискриминанта квадратного уравнения
24 | */
25 | fun discriminant(a: Double, b: Double, c: Double) = sqr(b) - 4 * a * c
26 |
27 | /**
28 | * Пример
29 | *
30 | * Поиск одного из корней квадратного уравнения
31 | */
32 | fun quadraticEquationRoot(a: Double, b: Double, c: Double) =
33 | (-b + sqrt(discriminant(a, b, c))) / (2 * a)
34 |
35 | /**
36 | * Пример
37 | *
38 | * Поиск произведения корней квадратного уравнения
39 | */
40 | fun quadraticRootProduct(a: Double, b: Double, c: Double): Double {
41 | val sd = sqrt(discriminant(a, b, c))
42 | val x1 = (-b + sd) / (2 * a)
43 | val x2 = (-b - sd) / (2 * a)
44 | return x1 * x2 // Результат
45 | }
46 |
47 | /**
48 | * Пример главной функции
49 | */
50 | fun main(args: Array) {
51 | val x1x2 = quadraticRootProduct(1.0, 13.0, 42.0)
52 | println("Root product: $x1x2")
53 | }
54 |
55 | /**
56 | * Тривиальная
57 | *
58 | * Пользователь задает время в часах, минутах и секундах, например, 8:20:35.
59 | * Рассчитать время в секундах, прошедшее с начала суток (30035 в данном случае).
60 | */
61 | fun seconds(hours: Int, minutes: Int, seconds: Int): Int = TODO()
62 |
63 | /**
64 | * Тривиальная
65 | *
66 | * Пользователь задает длину отрезка в саженях, аршинах и вершках (например, 8 саженей 2 аршина 11 вершков).
67 | * Определить длину того же отрезка в метрах (в данном случае 18.98).
68 | * 1 сажень = 3 аршина = 48 вершков, 1 вершок = 4.445 см.
69 | */
70 | fun lengthInMeters(sagenes: Int, arshins: Int, vershoks: Int): Double = TODO()
71 |
72 | /**
73 | * Тривиальная
74 | *
75 | * Пользователь задает угол в градусах, минутах и секундах (например, 36 градусов 14 минут 35 секунд).
76 | * Вывести значение того же угла в радианах (например, 0.63256).
77 | */
78 | fun angleInRadian(deg: Int, min: Int, sec: Int): Double = TODO()
79 |
80 | /**
81 | * Тривиальная
82 | *
83 | * Найти длину отрезка, соединяющего точки на плоскости с координатами (x1, y1) и (x2, y2).
84 | * Например, расстояние между (3, 0) и (0, 4) равно 5
85 | */
86 | fun trackLength(x1: Double, y1: Double, x2: Double, y2: Double): Double = TODO()
87 |
88 | /**
89 | * Простая
90 | *
91 | * Пользователь задает целое число, большее 100 (например, 3801).
92 | * Определить третью цифру справа в этом числе (в данном случае 8).
93 | */
94 | fun thirdDigit(number: Int): Int = TODO()
95 |
96 | /**
97 | * Простая
98 | *
99 | * Поезд вышел со станции отправления в h1 часов m1 минут (например в 9:25) и
100 | * прибыл на станцию назначения в h2 часов m2 минут того же дня (например в 13:01).
101 | * Определите время поезда в пути в минутах (в данном случае 216).
102 | */
103 | fun travelMinutes(hoursDepart: Int, minutesDepart: Int, hoursArrive: Int, minutesArrive: Int): Int = TODO()
104 |
105 | /**
106 | * Простая
107 | *
108 | * Человек положил в банк сумму в s рублей под p% годовых (проценты начисляются в конце года).
109 | * Сколько денег будет на счету через 3 года (с учётом сложных процентов)?
110 | * Например, 100 рублей под 10% годовых превратятся в 133.1 рубля
111 | */
112 | fun accountInThreeYears(initial: Int, percent: Int): Double = TODO()
113 |
114 | /**
115 | * Простая
116 | *
117 | * Пользователь задает целое трехзначное число (например, 478).
118 | * Необходимо вывести число, полученное из заданного перестановкой цифр в обратном порядке (например, 874).
119 | */
120 | fun numberRevert(number: Int): Int = TODO()
121 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/lesson2/task1/IfElse.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson2.task1
3 |
4 | import lesson1.task1.discriminant
5 | import kotlin.math.max
6 | import kotlin.math.sqrt
7 |
8 | /**
9 | * Пример
10 | *
11 | * Найти число корней квадратного уравнения ax^2 + bx + c = 0
12 | */
13 | fun quadraticRootNumber(a: Double, b: Double, c: Double): Int {
14 | val discriminant = discriminant(a, b, c)
15 | return when {
16 | discriminant > 0.0 -> 2
17 | discriminant == 0.0 -> 1
18 | else -> 0
19 | }
20 | }
21 |
22 | /**
23 | * Пример
24 | *
25 | * Получить строковую нотацию для оценки по пятибалльной системе
26 | */
27 | fun gradeNotation(grade: Int): String = when (grade) {
28 | 5 -> "отлично"
29 | 4 -> "хорошо"
30 | 3 -> "удовлетворительно"
31 | 2 -> "неудовлетворительно"
32 | else -> "несуществующая оценка $grade"
33 | }
34 |
35 | /**
36 | * Пример
37 | *
38 | * Найти наименьший корень биквадратного уравнения ax^4 + bx^2 + c = 0
39 | */
40 | fun minBiRoot(a: Double, b: Double, c: Double): Double {
41 | // 1: в главной ветке if выполняется НЕСКОЛЬКО операторов
42 | if (a == 0.0) {
43 | if (b == 0.0) return Double.NaN // ... и ничего больше не делать
44 | val bc = -c / b
45 | if (bc < 0.0) return Double.NaN // ... и ничего больше не делать
46 | return -sqrt(bc)
47 | // Дальше функция при a == 0.0 не идёт
48 | }
49 | val d = discriminant(a, b, c) // 2
50 | if (d < 0.0) return Double.NaN // 3
51 | // 4
52 | val y1 = (-b + sqrt(d)) / (2 * a)
53 | val y2 = (-b - sqrt(d)) / (2 * a)
54 | val y3 = max(y1, y2) // 5
55 | if (y3 < 0.0) return Double.NaN // 6
56 | return -sqrt(y3) // 7
57 | }
58 |
59 | /**
60 | * Простая
61 | *
62 | * Мой возраст. Для заданного 0 < n < 200, рассматриваемого как возраст человека,
63 | * вернуть строку вида: «21 год», «32 года», «12 лет».
64 | */
65 | fun ageDescription(age: Int): String = TODO()
66 |
67 | /**
68 | * Простая
69 | *
70 | * Путник двигался t1 часов со скоростью v1 км/час, затем t2 часов — со скоростью v2 км/час
71 | * и t3 часов — со скоростью v3 км/час.
72 | * Определить, за какое время он одолел первую половину пути?
73 | */
74 | fun timeForHalfWay(t1: Double, v1: Double,
75 | t2: Double, v2: Double,
76 | t3: Double, v3: Double): Double = TODO()
77 |
78 | /**
79 | * Простая
80 | *
81 | * Нa шахматной доске стоят черный король и две белые ладьи (ладья бьет по горизонтали и вертикали).
82 | * Определить, не находится ли король под боем, а если есть угроза, то от кого именно.
83 | * Вернуть 0, если угрозы нет, 1, если угроза только от первой ладьи, 2, если только от второй ладьи,
84 | * и 3, если угроза от обеих ладей.
85 | * Считать, что ладьи не могут загораживать друг друга
86 | */
87 | fun whichRookThreatens(kingX: Int, kingY: Int,
88 | rookX1: Int, rookY1: Int,
89 | rookX2: Int, rookY2: Int): Int = TODO()
90 |
91 | /**
92 | * Простая
93 | *
94 | * На шахматной доске стоят черный король и белые ладья и слон
95 | * (ладья бьет по горизонтали и вертикали, слон — по диагоналям).
96 | * Проверить, есть ли угроза королю и если есть, то от кого именно.
97 | * Вернуть 0, если угрозы нет, 1, если угроза только от ладьи, 2, если только от слона,
98 | * и 3, если угроза есть и от ладьи и от слона.
99 | * Считать, что ладья и слон не могут загораживать друг друга.
100 | */
101 | fun rookOrBishopThreatens(kingX: Int, kingY: Int,
102 | rookX: Int, rookY: Int,
103 | bishopX: Int, bishopY: Int): Int = TODO()
104 |
105 | /**
106 | * Простая
107 | *
108 | * Треугольник задан длинами своих сторон a, b, c.
109 | * Проверить, является ли данный треугольник остроугольным (вернуть 0),
110 | * прямоугольным (вернуть 1) или тупоугольным (вернуть 2).
111 | * Если такой треугольник не существует, вернуть -1.
112 | */
113 | fun triangleKind(a: Double, b: Double, c: Double): Int = TODO()
114 |
115 | /**
116 | * Средняя
117 | *
118 | * Даны четыре точки на одной прямой: A, B, C и D.
119 | * Координаты точек a, b, c, d соответственно, b >= a, d >= c.
120 | * Найти длину пересечения отрезков AB и CD.
121 | * Если пересечения нет, вернуть -1.
122 | */
123 | fun segmentLength(a: Int, b: Int, c: Int, d: Int): Int = TODO()
124 |
--------------------------------------------------------------------------------
/src/lesson10/task1/Regex.kt:
--------------------------------------------------------------------------------
1 | package lesson10.task1
2 |
3 | import lesson10.task1.Expression.Operation.*
4 | import java.io.File
5 | import java.util.regex.Pattern
6 |
7 | /**
8 | * Пример
9 | *
10 | * Во входном файле с именем inputName
11 | * содержится строчка, содержащая описание функции от x, например:
12 | *
13 | * 3*x*x - 2 / x + 7 -x
14 | *
15 | * В списке values содержатся целочисленные значения аргумента x, например, (1, 2, -1)
16 | *
17 | * Вернуть ассоциативный массив (map), содержащий отображение заданных значений аргумента
18 | * в значение заданной в файле функции, в данном случае
19 | *
20 | * (1 to 7, 2 to 16, -1 to 13)
21 | *
22 | * В функции могут присутствовать четыре арифметических действия и круглые скобки.
23 | * Обратите внимание, что функция является целочисленной,
24 | * то есть деление также следует трактовать как целочисленное.
25 | */
26 | fun parseExpr(inputName: String, values: List): Map {
27 | val expr = File(inputName).readLines().firstOrNull()?.parseExpr() ?: throw IllegalArgumentException()
28 | val result = mutableMapOf()
29 | for (value in values) {
30 | result[value] = expr.calculate(value)
31 | }
32 | return result
33 | }
34 |
35 | fun String.parseExpr(): Expression {
36 | val pattern = Pattern.compile("""x|\+|-|\*|/|\(|\)|\d+| +?|.+?""")
37 | val matcher = pattern.matcher(this)
38 | val groups = mutableListOf()
39 | while (matcher.find()) {
40 | val group = matcher.group()
41 | if (group[0] != ' ') {
42 | groups.add(group)
43 | }
44 | }
45 | return Parser(groups).parse()
46 | }
47 |
48 | sealed class Expression {
49 | object Variable : Expression()
50 |
51 | class Constant(val value: Int) : Expression()
52 |
53 | enum class Operation {
54 | PLUS,
55 | MINUS,
56 | TIMES,
57 | DIV;
58 | }
59 |
60 | class Binary(val left: Expression, val op: Operation, val right: Expression) : Expression()
61 |
62 | class Negate(val arg: Expression) : Expression()
63 |
64 | fun calculate(x: Int): Int = when (this) {
65 | Variable -> x
66 | is Constant -> value
67 | is Binary -> when (op) {
68 | PLUS -> left.calculate(x) + right.calculate(x)
69 | MINUS -> left.calculate(x) - right.calculate(x)
70 | TIMES -> left.calculate(x) * right.calculate(x)
71 | DIV -> left.calculate(x) / right.calculate(x)
72 | }
73 | is Negate -> -arg.calculate(x)
74 | }
75 | }
76 |
77 | class Parser(private val groups: List) {
78 | private var pos = 0
79 |
80 | fun parse(): Expression {
81 | val result = parseExpression()
82 | if (pos < groups.size) {
83 | throw IllegalStateException("Unexpected expression remainder: ${groups.subList(pos, groups.size)}")
84 | }
85 | return result
86 | }
87 |
88 | private fun parseExpression(): Expression {
89 | var left = parseItem()
90 | while (pos < groups.size) {
91 | val op = operationMap[groups[pos]]
92 | when (op) {
93 | PLUS, MINUS -> {
94 | pos++
95 | val right = parseItem()
96 | left = Expression.Binary(left, op, right)
97 | }
98 | else -> return left
99 | }
100 | }
101 | return left
102 | }
103 |
104 | private fun parseItem(): Expression {
105 | var left = parseFactor()
106 | while (pos < groups.size) {
107 | val op = operationMap[groups[pos]]
108 | when (op) {
109 | TIMES, DIV -> {
110 | pos++
111 | val right = parseFactor()
112 | left = Expression.Binary(left, op, right)
113 | }
114 | else -> return left
115 | }
116 | }
117 | return left
118 | }
119 |
120 | private fun parseFactor(): Expression =
121 | if (pos >= groups.size) throw IllegalStateException("Unexpected expression end")
122 | else {
123 | val group = groups[pos++]
124 | when (group) {
125 | "x" -> Expression.Variable
126 | "-" -> Expression.Negate(parseFactor())
127 | "(" -> {
128 | val arg = parseExpression()
129 | val next = groups[pos++]
130 | if (next == ")") arg
131 | else throw IllegalStateException(") expected instead of $next")
132 | }
133 | else -> Expression.Constant(group.toInt())
134 | }
135 | }
136 |
137 | private val operationMap = mapOf("+" to PLUS, "-" to MINUS, "*" to TIMES, "/" to DIV)
138 | }
--------------------------------------------------------------------------------
/src/lesson3/task1/Loop.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson3.task1
3 |
4 | import kotlin.math.sqrt
5 |
6 | /**
7 | * Пример
8 | *
9 | * Вычисление факториала
10 | */
11 | fun factorial(n: Int): Double {
12 | var result = 1.0
13 | for (i in 1..n) {
14 | result = result * i // Please do not fix in master
15 | }
16 | return result
17 | }
18 |
19 | /**
20 | * Пример
21 | *
22 | * Проверка числа на простоту -- результат true, если число простое
23 | */
24 | fun isPrime(n: Int): Boolean {
25 | if (n < 2) return false
26 | if (n == 2) return true
27 | if (n % 2 == 0) return false
28 | for (m in 3..sqrt(n.toDouble()).toInt() step 2) {
29 | if (n % m == 0) return false
30 | }
31 | return true
32 | }
33 |
34 | /**
35 | * Пример
36 | *
37 | * Проверка числа на совершенность -- результат true, если число совершенное
38 | */
39 | fun isPerfect(n: Int): Boolean {
40 | var sum = 1
41 | for (m in 2..n/2) {
42 | if (n % m > 0) continue
43 | sum += m
44 | if (sum > n) break
45 | }
46 | return sum == n
47 | }
48 |
49 | /**
50 | * Пример
51 | *
52 | * Найти число вхождений цифры m в число n
53 | */
54 | fun digitCountInNumber(n: Int, m: Int): Int =
55 | when {
56 | n == m -> 1
57 | n < 10 -> 0
58 | else -> digitCountInNumber(n / 10, m) + digitCountInNumber(n % 10, m)
59 | }
60 |
61 | /**
62 | * Тривиальная
63 | *
64 | * Найти количество цифр в заданном числе n.
65 | * Например, число 1 содержит 1 цифру, 456 -- 3 цифры, 65536 -- 5 цифр.
66 | *
67 | * Использовать операции со строками в этой задаче запрещается.
68 | */
69 | fun digitNumber(n: Int): Int = TODO()
70 |
71 | /**
72 | * Простая
73 | *
74 | * Найти число Фибоначчи из ряда 1, 1, 2, 3, 5, 8, 13, 21, ... с номером n.
75 | * Ряд Фибоначчи определён следующим образом: fib(1) = 1, fib(2) = 1, fib(n+2) = fib(n) + fib(n+1)
76 | */
77 | fun fib(n: Int): Int = TODO()
78 |
79 | /**
80 | * Простая
81 | *
82 | * Для заданных чисел m и n найти наименьшее общее кратное, то есть,
83 | * минимальное число k, которое делится и на m и на n без остатка
84 | */
85 | fun lcm(m: Int, n: Int): Int = TODO()
86 |
87 | /**
88 | * Простая
89 | *
90 | * Для заданного числа n > 1 найти минимальный делитель, превышающий 1
91 | */
92 | fun minDivisor(n: Int): Int = TODO()
93 |
94 | /**
95 | * Простая
96 | *
97 | * Для заданного числа n > 1 найти максимальный делитель, меньший n
98 | */
99 | fun maxDivisor(n: Int): Int = TODO()
100 |
101 | /**
102 | * Простая
103 | *
104 | * Определить, являются ли два заданных числа m и n взаимно простыми.
105 | * Взаимно простые числа не имеют общих делителей, кроме 1.
106 | * Например, 25 и 49 взаимно простые, а 6 и 8 -- нет.
107 | */
108 | fun isCoPrime(m: Int, n: Int): Boolean = TODO()
109 |
110 | /**
111 | * Простая
112 | *
113 | * Для заданных чисел m и n, m <= n, определить, имеется ли хотя бы один точный квадрат между m и n,
114 | * то есть, существует ли такое целое k, что m <= k*k <= n.
115 | * Например, для интервала 21..28 21 <= 5*5 <= 28, а для интервала 51..61 квадрата не существует.
116 | */
117 | fun squareBetweenExists(m: Int, n: Int): Boolean = TODO()
118 |
119 | /**
120 | * Средняя
121 | *
122 | * Гипотеза Коллатца. Рекуррентная последовательность чисел задана следующим образом:
123 | *
124 | * ЕСЛИ (X четное)
125 | * Xслед = X /2
126 | * ИНАЧЕ
127 | * Xслед = 3 * X + 1
128 | *
129 | * например
130 | * 15 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1 4 2 1 4 2 1 ...
131 | * Данная последовательность рано или поздно встречает X == 1.
132 | * Написать функцию, которая находит, сколько шагов требуется для
133 | * этого для какого-либо начального X > 0.
134 | */
135 | fun collatzSteps(x: Int): Int = TODO()
136 |
137 | /**
138 | * Средняя
139 | *
140 | * Для заданного x рассчитать с заданной точностью eps
141 | * sin(x) = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + ...
142 | * Нужную точность считать достигнутой, если очередной член ряда меньше eps по модулю
143 | */
144 | fun sin(x: Double, eps: Double): Double = TODO()
145 |
146 | /**
147 | * Средняя
148 | *
149 | * Для заданного x рассчитать с заданной точностью eps
150 | * cos(x) = 1 - x^2 / 2! + x^4 / 4! - x^6 / 6! + ...
151 | * Нужную точность считать достигнутой, если очередной член ряда меньше eps по модулю
152 | */
153 | fun cos(x: Double, eps: Double): Double = TODO()
154 |
155 | /**
156 | * Средняя
157 | *
158 | * Поменять порядок цифр заданного числа n на обратный: 13478 -> 87431.
159 | *
160 | * Использовать операции со строками в этой задаче запрещается.
161 | */
162 | fun revert(n: Int): Int = TODO()
163 |
164 | /**
165 | * Средняя
166 | *
167 | * Проверить, является ли заданное число n палиндромом:
168 | * первая цифра равна последней, вторая -- предпоследней и так далее.
169 | * 15751 -- палиндром, 3653 -- нет.
170 | *
171 | * Использовать операции со строками в этой задаче запрещается.
172 | */
173 | fun isPalindrome(n: Int): Boolean = TODO()
174 |
175 | /**
176 | * Средняя
177 | *
178 | * Для заданного числа n определить, содержит ли оно различающиеся цифры.
179 | * Например, 54 и 323 состоят из разных цифр, а 111 и 0 из одинаковых.
180 | *
181 | * Использовать операции со строками в этой задаче запрещается.
182 | */
183 | fun hasDifferentDigits(n: Int): Boolean = TODO()
184 |
185 | /**
186 | * Сложная
187 | *
188 | * Найти n-ю цифру последовательности из квадратов целых чисел:
189 | * 149162536496481100121144...
190 | * Например, 2-я цифра равна 4, 7-я 5, 12-я 6.
191 | *
192 | * Использовать операции со строками в этой задаче запрещается.
193 | */
194 | fun squareSequenceDigit(n: Int): Int = TODO()
195 |
196 | /**
197 | * Сложная
198 | *
199 | * Найти n-ю цифру последовательности из чисел Фибоначчи (см. функцию fib выше):
200 | * 1123581321345589144...
201 | * Например, 2-я цифра равна 1, 9-я 2, 14-я 5.
202 | *
203 | * Использовать операции со строками в этой задаче запрещается.
204 | */
205 | fun fibSequenceDigit(n: Int): Int = TODO()
206 |
--------------------------------------------------------------------------------
/test/lesson6/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson6.task1
2 |
3 | import org.junit.jupiter.api.Assertions.assertEquals
4 | import org.junit.jupiter.api.Assertions.assertThrows
5 | import org.junit.jupiter.api.Tag
6 | import org.junit.jupiter.api.Test
7 |
8 | class Tests {
9 | @Test
10 | @Tag("Example")
11 | fun timeStrToSeconds() {
12 | assertEquals(36000, timeStrToSeconds("10:00:00"))
13 | assertEquals(41685, timeStrToSeconds("11:34:45"))
14 | assertEquals(86399, timeStrToSeconds("23:59:59"))
15 | }
16 |
17 | @Test
18 | @Tag("Example")
19 | fun twoDigitStr() {
20 | assertEquals("00", twoDigitStr(0))
21 | assertEquals("09", twoDigitStr(9))
22 | assertEquals("10", twoDigitStr(10))
23 | assertEquals("99", twoDigitStr(99))
24 | }
25 |
26 | @Test
27 | @Tag("Example")
28 | fun timeSecondsToStr() {
29 | assertEquals("10:00:00", timeSecondsToStr(36000))
30 | assertEquals("11:34:45", timeSecondsToStr(41685))
31 | assertEquals("23:59:59", timeSecondsToStr(86399))
32 | }
33 |
34 | @Test
35 | @Tag("Normal")
36 | fun dateStrToDigit() {
37 | assertEquals("15.07.2016", dateStrToDigit("15 июля 2016"))
38 | assertEquals("", dateStrToDigit("3 мартобря 1918"))
39 | assertEquals("18.11.2018", dateStrToDigit("18 ноября 2018"))
40 | assertEquals("", dateStrToDigit("23"))
41 | assertEquals("03.04.2011", dateStrToDigit("3 апреля 2011"))
42 | assertEquals("", dateStrToDigit("32 сентября 2011"))
43 | assertEquals("", dateStrToDigit("29 февраля 1993"))
44 | }
45 |
46 | @Test
47 | @Tag("Normal")
48 | fun dateDigitToStr() {
49 | assertEquals("15 июля 2016", dateDigitToStr("15.07.2016"))
50 | assertEquals("", dateDigitToStr("01.02.20.19"))
51 | assertEquals("", dateDigitToStr("28.00.2000"))
52 | assertEquals("3 апреля 2011", dateDigitToStr("03.04.2011"))
53 | assertEquals("", dateDigitToStr("ab.cd.ef"))
54 | assertEquals("", dateDigitToStr("32.09.2011"))
55 | assertEquals("", dateDigitToStr("29.02.1993"))
56 | }
57 |
58 | @Test
59 | @Tag("Normal")
60 | fun flattenPhoneNumber() {
61 | assertEquals("+79211234567", flattenPhoneNumber("+7 (921) 123-45-67"))
62 | assertEquals("123456798", flattenPhoneNumber("12 -- 34- 5 -- 67 -98"))
63 | assertEquals("", flattenPhoneNumber("ab-123"))
64 | assertEquals("+12345", flattenPhoneNumber("+12 (3) 4-5"))
65 | assertEquals("", flattenPhoneNumber("134_+874"))
66 | }
67 |
68 | @Test
69 | @Tag("Normal")
70 | fun bestLongJump() {
71 | assertEquals(717, bestLongJump("706 % - 717 - 703"))
72 | assertEquals(-1, bestLongJump("% - - % -"))
73 | assertEquals(754, bestLongJump("700 717 707 % 754"))
74 | assertEquals(-1, bestLongJump("700 + 700"))
75 |
76 | }
77 |
78 | @Test
79 | @Tag("Hard")
80 | fun bestHighJump() {
81 | assertEquals(226, bestHighJump("226 +"))
82 | assertEquals(-1, bestHighJump("???"))
83 | assertEquals(230, bestHighJump("220 + 224 %+ 228 %- 230 + 232 %%- 234 %"))
84 | }
85 |
86 | @Test
87 | @Tag("Hard")
88 | fun plusMinus() {
89 | assertEquals(0, plusMinus("0"))
90 | assertEquals(4, plusMinus("2 + 2"))
91 | assertEquals(6, plusMinus("2 + 31 - 40 + 13"))
92 | assertEquals(-1, plusMinus("0 - 1"))
93 | assertThrows(IllegalArgumentException::class.java) { plusMinus("+2") }
94 | assertThrows(IllegalArgumentException::class.java) { plusMinus("+ 4") }
95 | assertThrows(IllegalArgumentException::class.java) { plusMinus("4 - -2") }
96 | assertThrows(IllegalArgumentException::class.java) { plusMinus("44 - - 12") }
97 | assertThrows(IllegalArgumentException::class.java) { plusMinus("4 - + 12") }
98 | }
99 |
100 | @Test
101 | @Tag("Hard")
102 | fun firstDuplicateIndex() {
103 | assertEquals(-1, firstDuplicateIndex("Привет"))
104 | assertEquals(9, firstDuplicateIndex("Он пошёл в в школу"))
105 | assertEquals(40, firstDuplicateIndex("Яблоко упало на ветку с ветки оно упало на на землю"))
106 | assertEquals(9, firstDuplicateIndex("Мы пошли прямо Прямо располагался магазин"))
107 | }
108 |
109 | @Test
110 | @Tag("Hard")
111 | fun mostExpensive() {
112 | assertEquals("", mostExpensive(""))
113 | assertEquals("Курица", mostExpensive("Хлеб 39.9; Молоко 62.5; Курица 184.0; Конфеты 89.9"))
114 | assertEquals("Вино", mostExpensive("Вино 255.0"))
115 | }
116 |
117 | @Test
118 | @Tag("Hard")
119 | fun fromRoman() {
120 | assertEquals(1, fromRoman("I"))
121 | assertEquals(3000, fromRoman("MMM"))
122 | assertEquals(1978, fromRoman("MCMLXXVIII"))
123 | assertEquals(694, fromRoman("DCXCIV"))
124 | assertEquals(49, fromRoman("XLIX"))
125 | assertEquals(-1, fromRoman("Z"))
126 | }
127 |
128 | @Test
129 | @Tag("Impossible")
130 | fun computeDeviceCells() {
131 | assertEquals(listOf(0, 0, 0, 0, 0, 1, 1, 1, 1, 1), computeDeviceCells(10, "+>+>+>+>+", 10000))
132 | assertEquals(listOf(-1, -1, -1, -1, -1, 0, 0, 0, 0, 0), computeDeviceCells(10, "<-<-<-<-<-", 10000))
133 | assertEquals(listOf(1, 1, 1, 1, 1, 0, 0, 0, 0, 0), computeDeviceCells(10, "- <<<<< +[>+]", 10000))
134 | assertEquals(listOf(0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0),
135 | computeDeviceCells(11, "<<<<< + >>>>>>>>>> --[<-] >+[>+] >++[--< <[<] >+[>+] >++]", 10000))
136 |
137 | assertEquals(listOf(0, 0, 0, 0, 0, 1, 1, 0, 0, 0), computeDeviceCells(10, "+>+>+>+>+", 4))
138 | assertEquals(listOf(0, 0, -1, -1, -1, 0, 0, 0, 0, 0), computeDeviceCells(10, "<-<-<-<-<-", 6))
139 | assertEquals(listOf(1, 1, 1, 0, 0, -1, 0, 0, 0, 0), computeDeviceCells(10, "- <<<<< +[>+]", 17))
140 | assertEquals(listOf(0, 6, 5, 4, 3, 2, 1, 0, -1, -1, -2),
141 | computeDeviceCells(11, "<<<<< + >>>>>>>>>> --[<-] >+[>+] >++[--< <[<] >+[>+] >++]", 256))
142 | assertThrows(IllegalArgumentException::class.java) { computeDeviceCells(10, "===", 3) }
143 | assertThrows(IllegalArgumentException::class.java) { computeDeviceCells(10, "+>+>[+>", 3) }
144 | assertThrows(IllegalStateException::class.java) { computeDeviceCells(20, ">>>>>>>>>>>>>", 12) }
145 | }
146 | }
--------------------------------------------------------------------------------
/src/lesson8/task1/Geometry.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson8.task1
3 |
4 | import lesson1.task1.sqr
5 | import kotlin.math.PI
6 | import kotlin.math.cos
7 | import kotlin.math.sin
8 | import kotlin.math.sqrt
9 |
10 | /**
11 | * Точка на плоскости
12 | */
13 | data class Point(val x: Double, val y: Double) {
14 | /**
15 | * Пример
16 | *
17 | * Рассчитать (по известной формуле) расстояние между двумя точками
18 | */
19 | fun distance(other: Point): Double = sqrt(sqr(x - other.x) + sqr(y - other.y))
20 | }
21 |
22 | /**
23 | * Треугольник, заданный тремя точками (a, b, c, см. constructor ниже).
24 | * Эти три точки хранятся в множестве points, их порядок не имеет значения.
25 | */
26 | class Triangle private constructor(private val points: Set) {
27 |
28 | private val pointList = points.toList()
29 |
30 | val a: Point get() = pointList[0]
31 |
32 | val b: Point get() = pointList[1]
33 |
34 | val c: Point get() = pointList[2]
35 |
36 | constructor(a: Point, b: Point, c: Point): this(linkedSetOf(a, b, c))
37 | /**
38 | * Пример: полупериметр
39 | */
40 | fun halfPerimeter() = (a.distance(b) + b.distance(c) + c.distance(a)) / 2.0
41 |
42 | /**
43 | * Пример: площадь
44 | */
45 | fun area(): Double {
46 | val p = halfPerimeter()
47 | return sqrt(p * (p - a.distance(b)) * (p - b.distance(c)) * (p - c.distance(a)))
48 | }
49 |
50 | /**
51 | * Пример: треугольник содержит точку
52 | */
53 | fun contains(p: Point): Boolean {
54 | val abp = Triangle(a, b, p)
55 | val bcp = Triangle(b, c, p)
56 | val cap = Triangle(c, a, p)
57 | return abp.area() + bcp.area() + cap.area() <= area()
58 | }
59 |
60 | override fun equals(other: Any?) = other is Triangle && points == other.points
61 |
62 | override fun hashCode() = points.hashCode()
63 |
64 | override fun toString() = "Triangle(a = $a, b = $b, c = $c)"
65 | }
66 |
67 | /**
68 | * Окружность с заданным центром и радиусом
69 | */
70 | data class Circle(val center: Point, val radius: Double) {
71 | /**
72 | * Простая
73 | *
74 | * Рассчитать расстояние между двумя окружностями.
75 | * Расстояние между непересекающимися окружностями рассчитывается как
76 | * расстояние между их центрами минус сумма их радиусов.
77 | * Расстояние между пересекающимися окружностями считать равным 0.0.
78 | */
79 | fun distance(other: Circle): Double = TODO()
80 |
81 | /**
82 | * Тривиальная
83 | *
84 | * Вернуть true, если и только если окружность содержит данную точку НА себе или ВНУТРИ себя
85 | */
86 | fun contains(p: Point): Boolean = TODO()
87 | }
88 |
89 | /**
90 | * Отрезок между двумя точками
91 | */
92 | data class Segment(val begin: Point, val end: Point) {
93 | override fun equals(other: Any?) =
94 | other is Segment && (begin == other.begin && end == other.end || end == other.begin && begin == other.end)
95 |
96 | override fun hashCode() =
97 | begin.hashCode() + end.hashCode()
98 | }
99 |
100 | /**
101 | * Средняя
102 | *
103 | * Дано множество точек. Вернуть отрезок, соединяющий две наиболее удалённые из них.
104 | * Если в множестве менее двух точек, бросить IllegalArgumentException
105 | */
106 | fun diameter(vararg points: Point): Segment = TODO()
107 |
108 | /**
109 | * Простая
110 | *
111 | * Построить окружность по её диаметру, заданному двумя точками
112 | * Центр её должен находиться посередине между точками, а радиус составлять половину расстояния между ними
113 | */
114 | fun circleByDiameter(diameter: Segment): Circle = TODO()
115 |
116 | /**
117 | * Прямая, заданная точкой point и углом наклона angle (в радианах) по отношению к оси X.
118 | * Уравнение прямой: (y - point.y) * cos(angle) = (x - point.x) * sin(angle)
119 | * или: y * cos(angle) = x * sin(angle) + b, где b = point.y * cos(angle) - point.x * sin(angle).
120 | * Угол наклона обязан находиться в диапазоне от 0 (включительно) до PI (исключительно).
121 | */
122 | class Line private constructor(val b: Double, val angle: Double) {
123 | init {
124 | require(angle >= 0 && angle < PI) { "Incorrect line angle: $angle" }
125 | }
126 |
127 | constructor(point: Point, angle: Double): this(point.y * cos(angle) - point.x * sin(angle), angle)
128 |
129 | /**
130 | * Средняя
131 | *
132 | * Найти точку пересечения с другой линией.
133 | * Для этого необходимо составить и решить систему из двух уравнений (каждое для своей прямой)
134 | */
135 | fun crossPoint(other: Line): Point = TODO()
136 |
137 | override fun equals(other: Any?) = other is Line && angle == other.angle && b == other.b
138 |
139 | override fun hashCode(): Int {
140 | var result = b.hashCode()
141 | result = 31 * result + angle.hashCode()
142 | return result
143 | }
144 |
145 | override fun toString() = "Line(${cos(angle)} * y = ${sin(angle)} * x + $b)"
146 | }
147 |
148 | /**
149 | * Средняя
150 | *
151 | * Построить прямую по отрезку
152 | */
153 | fun lineBySegment(s: Segment): Line = TODO()
154 |
155 | /**
156 | * Средняя
157 | *
158 | * Построить прямую по двум точкам
159 | */
160 | fun lineByPoints(a: Point, b: Point): Line = TODO()
161 |
162 | /**
163 | * Сложная
164 | *
165 | * Построить серединный перпендикуляр по отрезку или по двум точкам
166 | */
167 | fun bisectorByPoints(a: Point, b: Point): Line = TODO()
168 |
169 | /**
170 | * Средняя
171 | *
172 | * Задан список из n окружностей на плоскости. Найти пару наименее удалённых из них.
173 | * Если в списке менее двух окружностей, бросить IllegalArgumentException
174 | */
175 | fun findNearestCirclePair(vararg circles: Circle): Pair = TODO()
176 |
177 | /**
178 | * Сложная
179 | *
180 | * Дано три различные точки. Построить окружность, проходящую через них
181 | * (все три точки должны лежать НА, а не ВНУТРИ, окружности).
182 | * Описание алгоритмов см. в Интернете
183 | * (построить окружность по трём точкам, или
184 | * построить окружность, описанную вокруг треугольника - эквивалентная задача).
185 | */
186 | fun circleByThreePoints(a: Point, b: Point, c: Point): Circle = TODO()
187 |
188 | /**
189 | * Очень сложная
190 | *
191 | * Дано множество точек на плоскости. Найти круг минимального радиуса,
192 | * содержащий все эти точки. Если множество пустое, бросить IllegalArgumentException.
193 | * Если множество содержит одну точку, вернуть круг нулевого радиуса с центром в данной точке.
194 | *
195 | * Примечание: в зависимости от ситуации, такая окружность может либо проходить через какие-либо
196 | * три точки данного множества, либо иметь своим диаметром отрезок,
197 | * соединяющий две самые удалённые точки в данном множестве.
198 | */
199 | fun minContainingCircle(vararg points: Point): Circle = TODO()
200 |
201 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | ru.spbstu
8 | kfirst
9 | 0.1.14
10 |
11 |
12 |
13 | UTF-8
14 |
15 |
16 | UTF-8
17 |
18 |
19 | 1.2.51
20 | 0.6.11
21 | 5.0.0
22 | 4.12.0
23 | 1.0.0
24 |
25 | 1.2
26 | 1.2
27 |
28 |
29 |
30 |
31 | org.jetbrains.kotlin
32 | kotlin-stdlib
33 | ${kotlin.version}
34 |
35 |
36 | org.jetbrains.kotlin
37 | kotlin-reflect
38 | ${kotlin.version}
39 |
40 |
41 | org.jetbrains.kotlin
42 | kotlin-runtime
43 | ${kotlin.version}
44 |
45 |
46 |
47 | org.junit.jupiter
48 | junit-jupiter-api
49 | ${junit.jupiter.version}
50 | test
51 |
52 |
53 | org.junit.jupiter
54 | junit-jupiter-engine
55 | ${junit.jupiter.version}
56 | test
57 |
58 |
59 | org.junit.vintage
60 | junit-vintage-engine
61 | ${junit.vintage.version}
62 | test
63 |
64 |
65 | org.jetbrains.kotlinx
66 | kotlinx-html-jvm
67 | ${kotlinx.html.version}
68 |
69 |
70 |
71 | src
72 | test
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-compiler-plugin
77 | 3.5.1
78 |
79 | 1.8
80 | 1.8
81 | 1.8
82 | 1.8
83 |
84 |
85 |
86 | maven-surefire-plugin
87 | 2.19.1
88 |
89 | true
90 |
91 |
92 |
93 | org.junit.platform
94 |
95 | junit-platform-surefire-provider
96 |
97 | ${junit.platform.version}
98 |
99 |
100 |
101 |
102 | kotlin-maven-plugin
103 | org.jetbrains.kotlin
104 | ${kotlin.version}
105 |
106 |
107 | compile
108 | process-sources
109 |
110 | compile
111 |
112 |
113 |
114 | test-compile
115 | process-test-sources
116 |
117 | test-compile
118 |
119 |
120 |
121 |
122 |
123 | kfirst-runner-plugin
124 | org.jetbrains.research
125 | 0.1.17
126 |
127 |
128 | lesson1.task1
129 |
130 | lesson2.task1
131 | lesson2.task2
132 |
133 | lesson3.task1
134 | lesson4.task1
135 | lesson5.task1
136 | lesson6.task1
137 | lesson7.task1
138 |
139 | lesson8.task1
140 | lesson8.task2
141 | lesson8.task3
142 |
143 | lesson9.task1
144 | lesson9.task2
145 |
146 | lesson10.task1
147 | lesson10.task2
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | jcenter
157 | jcenter
158 | https://jcenter.bintray.com
159 |
160 |
161 |
162 |
163 |
164 | bintray-vorpal-research-kotlin-maven
165 | vorpal-research-kotlin-maven
166 | http://dl.bintray.com/vorpal-research/kotlin-maven
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/src/lesson4/task1/List.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER", "ConvertCallChainIntoSequence")
2 |
3 | package lesson4.task1
4 |
5 | import lesson1.task1.discriminant
6 | import kotlin.math.sqrt
7 |
8 | /**
9 | * Пример
10 | *
11 | * Найти все корни уравнения x^2 = y
12 | */
13 | fun sqRoots(y: Double) =
14 | when {
15 | y < 0 -> listOf()
16 | y == 0.0 -> listOf(0.0)
17 | else -> {
18 | val root = sqrt(y)
19 | // Результат!
20 | listOf(-root, root)
21 | }
22 | }
23 |
24 | /**
25 | * Пример
26 | *
27 | * Найти все корни биквадратного уравнения ax^4 + bx^2 + c = 0.
28 | * Вернуть список корней (пустой, если корней нет)
29 | */
30 | fun biRoots(a: Double, b: Double, c: Double): List {
31 | if (a == 0.0) {
32 | return if (b == 0.0) listOf()
33 | else sqRoots(-c / b)
34 | }
35 | val d = discriminant(a, b, c)
36 | if (d < 0.0) return listOf()
37 | if (d == 0.0) return sqRoots(-b / (2 * a))
38 | val y1 = (-b + sqrt(d)) / (2 * a)
39 | val y2 = (-b - sqrt(d)) / (2 * a)
40 | return sqRoots(y1) + sqRoots(y2)
41 | }
42 |
43 | /**
44 | * Пример
45 | *
46 | * Выделить в список отрицательные элементы из заданного списка
47 | */
48 | fun negativeList(list: List): List {
49 | val result = mutableListOf()
50 | for (element in list) {
51 | if (element < 0) {
52 | result.add(element)
53 | }
54 | }
55 | return result
56 | }
57 |
58 | /**
59 | * Пример
60 | *
61 | * Изменить знак для всех положительных элементов списка
62 | */
63 | fun invertPositives(list: MutableList) {
64 | for (i in 0 until list.size) {
65 | val element = list[i]
66 | if (element > 0) {
67 | list[i] = -element
68 | }
69 | }
70 | }
71 |
72 | /**
73 | * Пример
74 | *
75 | * Из имеющегося списка целых чисел, сформировать список их квадратов
76 | */
77 | fun squares(list: List) = list.map { it * it }
78 |
79 | /**
80 | * Пример
81 | *
82 | * Из имеющихся целых чисел, заданного через vararg-параметр, сформировать массив их квадратов
83 | */
84 | fun squares(vararg array: Int) = squares(array.toList()).toTypedArray()
85 |
86 | /**
87 | * Пример
88 | *
89 | * По заданной строке str определить, является ли она палиндромом.
90 | * В палиндроме первый символ должен быть равен последнему, второй предпоследнему и т.д.
91 | * Одни и те же буквы в разном регистре следует считать равными с точки зрения данной задачи.
92 | * Пробелы не следует принимать во внимание при сравнении символов, например, строка
93 | * "А роза упала на лапу Азора" является палиндромом.
94 | */
95 | fun isPalindrome(str: String): Boolean {
96 | val lowerCase = str.toLowerCase().filter { it != ' ' }
97 | for (i in 0..lowerCase.length / 2) {
98 | if (lowerCase[i] != lowerCase[lowerCase.length - i - 1]) return false
99 | }
100 | return true
101 | }
102 |
103 | /**
104 | * Пример
105 | *
106 | * По имеющемуся списку целых чисел, например [3, 6, 5, 4, 9], построить строку с примером их суммирования:
107 | * 3 + 6 + 5 + 4 + 9 = 27 в данном случае.
108 | */
109 | fun buildSumExample(list: List) = list.joinToString(separator = " + ", postfix = " = ${list.sum()}")
110 |
111 | /**
112 | * Простая
113 | *
114 | * Найти модуль заданного вектора, представленного в виде списка v,
115 | * по формуле abs = sqrt(a1^2 + a2^2 + ... + aN^2).
116 | * Модуль пустого вектора считать равным 0.0.
117 | */
118 | fun abs(v: List): Double = TODO()
119 |
120 | /**
121 | * Простая
122 | *
123 | * Рассчитать среднее арифметическое элементов списка list. Вернуть 0.0, если список пуст
124 | */
125 | fun mean(list: List): Double = TODO()
126 |
127 | /**
128 | * Средняя
129 | *
130 | * Центрировать заданный список list, уменьшив каждый элемент на среднее арифметическое всех элементов.
131 | * Если список пуст, не делать ничего. Вернуть изменённый список.
132 | *
133 | * Обратите внимание, что данная функция должна изменять содержание списка list, а не его копии.
134 | */
135 | fun center(list: MutableList): MutableList = TODO()
136 |
137 | /**
138 | * Средняя
139 | *
140 | * Найти скалярное произведение двух векторов равной размерности,
141 | * представленные в виде списков a и b. Скалярное произведение считать по формуле:
142 | * C = a1b1 + a2b2 + ... + aNbN. Произведение пустых векторов считать равным 0.0.
143 | */
144 | fun times(a: List, b: List): Double = TODO()
145 |
146 | /**
147 | * Средняя
148 | *
149 | * Рассчитать значение многочлена при заданном x:
150 | * p(x) = p0 + p1*x + p2*x^2 + p3*x^3 + ... + pN*x^N.
151 | * Коэффициенты многочлена заданы списком p: (p0, p1, p2, p3, ..., pN).
152 | * Значение пустого многочлена равно 0.0 при любом x.
153 | */
154 | fun polynom(p: List, x: Double): Double = TODO()
155 |
156 | /**
157 | * Средняя
158 | *
159 | * В заданном списке list каждый элемент, кроме первого, заменить
160 | * суммой данного элемента и всех предыдущих.
161 | * Например: 1, 2, 3, 4 -> 1, 3, 6, 10.
162 | * Пустой список не следует изменять. Вернуть изменённый список.
163 | *
164 | * Обратите внимание, что данная функция должна изменять содержание списка list, а не его копии.
165 | */
166 | fun accumulate(list: MutableList): MutableList = TODO()
167 |
168 | /**
169 | * Средняя
170 | *
171 | * Разложить заданное натуральное число n > 1 на простые множители.
172 | * Результат разложения вернуть в виде списка множителей, например 75 -> (3, 5, 5).
173 | * Множители в списке должны располагаться по возрастанию.
174 | */
175 | fun factorize(n: Int): List = TODO()
176 |
177 | /**
178 | * Сложная
179 | *
180 | * Разложить заданное натуральное число n > 1 на простые множители.
181 | * Результат разложения вернуть в виде строки, например 75 -> 3*5*5
182 | * Множители в результирующей строке должны располагаться по возрастанию.
183 | */
184 | fun factorizeToString(n: Int): String = TODO()
185 |
186 | /**
187 | * Средняя
188 | *
189 | * Перевести заданное целое число n >= 0 в систему счисления с основанием base > 1.
190 | * Результат перевода вернуть в виде списка цифр в base-ичной системе от старшей к младшей,
191 | * например: n = 100, base = 4 -> (1, 2, 1, 0) или n = 250, base = 14 -> (1, 3, 12)
192 | */
193 | fun convert(n: Int, base: Int): List = TODO()
194 |
195 | /**
196 | * Сложная
197 | *
198 | * Перевести заданное целое число n >= 0 в систему счисления с основанием 1 < base < 37.
199 | * Результат перевода вернуть в виде строки, цифры более 9 представлять латинскими
200 | * строчными буквами: 10 -> a, 11 -> b, 12 -> c и так далее.
201 | * Например: n = 100, base = 4 -> 1210, n = 250, base = 14 -> 13c
202 | */
203 | fun convertToString(n: Int, base: Int): String = TODO()
204 |
205 | /**
206 | * Средняя
207 | *
208 | * Перевести число, представленное списком цифр digits от старшей к младшей,
209 | * из системы счисления с основанием base в десятичную.
210 | * Например: digits = (1, 3, 12), base = 14 -> 250
211 | */
212 | fun decimal(digits: List, base: Int): Int = TODO()
213 |
214 | /**
215 | * Сложная
216 | *
217 | * Перевести число, представленное цифровой строкой str,
218 | * из системы счисления с основанием base в десятичную.
219 | * Цифры более 9 представляются латинскими строчными буквами:
220 | * 10 -> a, 11 -> b, 12 -> c и так далее.
221 | * Например: str = "13c", base = 14 -> 250
222 | */
223 | fun decimalFromString(str: String, base: Int): Int = TODO()
224 |
225 | /**
226 | * Сложная
227 | *
228 | * Перевести натуральное число n > 0 в римскую систему.
229 | * Римские цифры: 1 = I, 4 = IV, 5 = V, 9 = IX, 10 = X, 40 = XL, 50 = L,
230 | * 90 = XC, 100 = C, 400 = CD, 500 = D, 900 = CM, 1000 = M.
231 | * Например: 23 = XXIII, 44 = XLIV, 100 = C
232 | */
233 | fun roman(n: Int): String = TODO()
234 |
235 | /**
236 | * Очень сложная
237 | *
238 | * Записать заданное натуральное число 1..999999 прописью по-русски.
239 | * Например, 375 = "триста семьдесят пять",
240 | * 23964 = "двадцать три тысячи девятьсот шестьдесят четыре"
241 | */
242 | fun russian(n: Int): String = TODO()
--------------------------------------------------------------------------------
/test/lesson8/task2/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson8.task2
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 | import kotlin.math.abs
7 |
8 | class Tests {
9 | @Test
10 | @Tag("Example")
11 | fun inside() {
12 | assertTrue(Square(1, 1).inside())
13 | assertTrue(Square(8, 8).inside())
14 | assertTrue(Square(1, 8).inside())
15 | assertFalse(Square(0, 0).inside())
16 | assertFalse(Square(0, 1).inside())
17 | assertFalse(Square(9, 4).inside())
18 | assertFalse(Square(6, 9).inside())
19 | assertFalse(Square(100, 1).inside())
20 | assertFalse(Square(7, -100).inside())
21 | }
22 |
23 | @Test
24 | @Tag("Easy")
25 | fun notation() {
26 | assertEquals("", Square(1, 0).notation())
27 | assertEquals("b3", Square(2, 3).notation())
28 | assertEquals("g6", Square(7, 6).notation())
29 | assertEquals("a8", Square(1, 8).notation())
30 | assertEquals("h1", Square(8, 1).notation())
31 | }
32 |
33 | @Test
34 | @Tag("Easy")
35 | fun square() {
36 | assertEquals(Square(3, 2), square("c2"))
37 | assertEquals(Square(5, 5), square("e5"))
38 | assertEquals(Square(6, 8), square("f8"))
39 | assertEquals(Square(4, 1), square("d1"))
40 | }
41 |
42 | @Test
43 | @Tag("Easy")
44 | fun rookMoveNumber() {
45 | assertEquals(0, rookMoveNumber(square("e3"), square("e3")))
46 | assertEquals(2, rookMoveNumber(square("c2"), square("b1")))
47 | assertEquals(2, rookMoveNumber(square("g8"), square("f6")))
48 | assertEquals(1, rookMoveNumber(square("a8"), square("g8")))
49 | assertEquals(1, rookMoveNumber(square("h3"), square("h8")))
50 | }
51 |
52 | private fun List.assertRookTrajectory(start: Square, end: Square, length: Int) {
53 | assertEquals(length + 1, size)
54 | assertEquals(start, first())
55 | assertEquals(end, last())
56 | for (i in 0..size - 2) {
57 | val previous = this[i]
58 | val next = this[i + 1]
59 | assertTrue(previous.row == next.row || previous.column == next.column)
60 | }
61 | }
62 |
63 | @Test
64 | @Tag("Normal")
65 | fun rookTrajectory() {
66 | assertEquals(listOf(square("g5")), rookTrajectory(square("g5"), square("g5")))
67 | rookTrajectory(square("c3"), square("h6")).assertRookTrajectory(square("c3"), square("h6"), 2)
68 | assertEquals(listOf(square("h2"), square("h7")), rookTrajectory(square("h2"), square("h7")))
69 | }
70 |
71 | @Test
72 | @Tag("Easy")
73 | fun bishopMoveNumber() {
74 | assertEquals(-1, bishopMoveNumber(square("a1"), square("g8")))
75 | assertEquals(-1, bishopMoveNumber(square("c1"), square("f3")))
76 | assertEquals(0, bishopMoveNumber(square("d4"), square("d4")))
77 | assertEquals(1, bishopMoveNumber(square("a3"), square("e7")))
78 | assertEquals(2, bishopMoveNumber(square("c1"), square("c7")))
79 | }
80 |
81 | private fun List.assertBishopTrajectory(start: Square, end: Square, length: Int) {
82 | assertEquals(length + 1, size)
83 | assertEquals(start, first())
84 | assertEquals(end, last())
85 | for (i in 0..size - 2) {
86 | val previous = this[i]
87 | val next = this[i + 1]
88 | assertTrue(abs(next.row - previous.row) == abs(next.column - previous.column))
89 | }
90 | }
91 |
92 | @Test
93 | @Tag("Hard")
94 | fun bishopTrajectory() {
95 | assertEquals(listOf(), bishopTrajectory(square("a1"), square("g8")))
96 | assertEquals(listOf(), bishopTrajectory(square("c1"), square("f3")))
97 | assertEquals(listOf(square("d4")), bishopTrajectory(square("d4"), square("d4")))
98 | assertEquals(listOf(square("a3"), square("e7")), bishopTrajectory(square("a3"), square("e7")))
99 | assertEquals(listOf(square("c1"), square("f4"), square("c7")), bishopTrajectory(square("c1"), square("c7")))
100 | assertEquals(listOf(square("f1"), square("c4"), square("f7")), bishopTrajectory(square("f1"), square("f7")))
101 | bishopTrajectory(square("d2"), square("e5")).assertBishopTrajectory(square("d2"), square("e5"), 2)
102 | }
103 |
104 | @Test
105 | @Tag("Normal")
106 | fun kingMoveNumber() {
107 | assertEquals(0, kingMoveNumber(square("e3"), square("e3")))
108 | assertEquals(1, kingMoveNumber(square("c2"), square("b1")))
109 | assertEquals(2, kingMoveNumber(square("g8"), square("f6")))
110 | assertEquals(6, kingMoveNumber(square("a8"), square("g8")))
111 | assertEquals(7, kingMoveNumber(square("a1"), square("h8")))
112 | }
113 |
114 | private fun List.assertKingTrajectory(start: Square, end: Square, length: Int) {
115 | assertEquals(length + 1, size)
116 | assertEquals(start, first())
117 | assertEquals(end, last())
118 | for (i in 0..size - 2) {
119 | val previous = this[i]
120 | val next = this[i + 1]
121 | assertTrue(abs(next.column - previous.column) <= 1)
122 | assertTrue(abs(next.row - previous.row) <= 1)
123 | }
124 | }
125 |
126 | @Test
127 | @Tag("Hard")
128 | fun kingTrajectory() {
129 | assertEquals(listOf(square("f3")), kingTrajectory(square("f3"), square("f3")))
130 | kingTrajectory(square("c2"), square("a6")).assertKingTrajectory(square("c2"), square("a6"), 4)
131 | assertEquals(listOf(square("h2"), square("g3"), square("f4"), square("e5"), square("d6"), square("c7")),
132 | kingTrajectory(square("h2"), square("c7")))
133 | }
134 |
135 | @Test
136 | @Tag("Hard")
137 | fun knightMoveNumber() {
138 | assertEquals(0, knightMoveNumber(square("d3"), square("d3")))
139 | assertEquals(1, knightMoveNumber(square("e4"), square("d6")))
140 | assertEquals(2, knightMoveNumber(square("f5"), square("g6")))
141 | assertEquals(3, knightMoveNumber(square("g6"), square("g3")))
142 | assertEquals(3, knightMoveNumber(square("d4"), square("a8")))
143 | assertEquals(4, knightMoveNumber(square("h7"), square("f5")))
144 | assertEquals(4, knightMoveNumber(square("g7"), square("h8")))
145 | assertEquals(6, knightMoveNumber(square("a8"), square("h1")))
146 | }
147 |
148 | private fun List.assertKnightTrajectory(start: Square, end: Square, length: Int) {
149 | assertEquals(length + 1, size)
150 | assertEquals(start, first())
151 | assertEquals(end, last())
152 | for (i in 0..size - 2) {
153 | val previous = this[i]
154 | val next = this[i + 1]
155 | assertTrue(
156 | abs(next.column - previous.column) == 2 && abs(next.row - previous.row) == 1 ||
157 | abs(next.column - previous.column) == 1 && abs(next.row - previous.row) == 2
158 | )
159 | }
160 | }
161 |
162 | @Test
163 | @Tag("Impossible")
164 | fun knightTrajectory() {
165 | assertEquals(listOf(square("d3")), knightTrajectory(square("d3"), square("d3")))
166 | assertEquals(listOf(square("e4"), square("d6")), knightTrajectory(square("e4"), square("d6")))
167 | knightTrajectory(square("f5"), square("g6")).assertKnightTrajectory(square("f5"), square("g6"), 2)
168 | knightTrajectory(square("g6"), square("g3")).assertKnightTrajectory(square("g6"), square("g3"), 3)
169 | knightTrajectory(square("d4"), square("a8")).assertKnightTrajectory(square("d4"), square("a8"), 3)
170 | knightTrajectory(square("h7"), square("f5")).assertKnightTrajectory(square("h7"), square("f5"), 4)
171 | knightTrajectory(square("g7"), square("h8")).assertKnightTrajectory(square("g7"), square("h8"), 4)
172 | knightTrajectory(square("a1"), square("a8")).assertKnightTrajectory(square("a1"), square("a8"), 5)
173 | knightTrajectory(square("a8"), square("h1")).assertKnightTrajectory(square("a8"), square("h1"), 6)
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/test/lesson3/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson3.task1
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 | import kotlin.math.PI
7 |
8 | class Tests {
9 | @Test
10 | @Tag("Example")
11 | fun factorial() {
12 | assertEquals(1.0, factorial(0), 1e-5)
13 | assertEquals(1.0, factorial(1), 1e-5)
14 | assertEquals(6.0, factorial(3), 1e-5)
15 | assertEquals(120.0, factorial(5), 1e-5)
16 | assertEquals(3628800.0, factorial(10), 1e-5)
17 | assertEquals(2.43290200817664E18, factorial(20), 1E10)
18 | }
19 |
20 | @Test
21 | @Tag("Example")
22 | fun isPrime() {
23 | assertFalse(isPrime(1))
24 | assertTrue(isPrime(2))
25 | assertTrue(isPrime(5))
26 | assertTrue(isPrime(11))
27 | assertFalse(isPrime(4))
28 | assertFalse(isPrime(9))
29 | assertFalse(isPrime(15))
30 | var count = 0
31 | for (n in 2..7919) {
32 | if (isPrime(n)) {
33 | count++
34 | }
35 | }
36 | assertEquals(1000, count)
37 | count = 0
38 | for (n in 2..1000000) {
39 | if (isPrime(n)) {
40 | count++
41 | }
42 | }
43 | assertEquals(78498, count)
44 | }
45 |
46 | @Test
47 | @Tag("Example")
48 | fun isPerfect() {
49 | assertTrue(isPerfect(6))
50 | assertTrue(isPerfect(28))
51 | assertFalse(isPerfect(100))
52 | }
53 |
54 | @Test
55 | @Tag("Example")
56 | fun digitCountInNumber() {
57 | assertEquals(1, digitCountInNumber(0, 0))
58 | assertEquals(1, digitCountInNumber(7, 7))
59 | assertEquals(0, digitCountInNumber(21, 3))
60 | assertEquals(1, digitCountInNumber(510, 5))
61 | assertEquals(3, digitCountInNumber(4784041, 4))
62 | assertEquals(4, digitCountInNumber(5373393, 3))
63 | }
64 |
65 | @Test
66 | @Tag("Trivial")
67 | fun digitNumber() {
68 | assertEquals(1, digitNumber(0))
69 | assertEquals(1, digitNumber(7))
70 | assertEquals(2, digitNumber(10))
71 | assertEquals(2, digitNumber(99))
72 | assertEquals(3, digitNumber(123))
73 | assertEquals(10, digitNumber(Int.MAX_VALUE))
74 | }
75 |
76 | @Test
77 | @Tag("Easy")
78 | fun fib() {
79 | assertEquals(1, fib(1))
80 | assertEquals(1, fib(2))
81 | assertEquals(2, fib(3))
82 | assertEquals(5, fib(5))
83 | assertEquals(21, fib(8))
84 | assertEquals(102334155, fib(40))
85 | assertEquals(1134903170, fib(45))
86 | assertEquals(1836311903, fib(46))
87 | // Just to calculate it
88 | fib(50)
89 | }
90 |
91 | @Test
92 | @Tag("Easy")
93 | fun lcm() {
94 | assertEquals(13, lcm(13, 13))
95 | assertEquals(8, lcm(2, 8))
96 | assertEquals(24, lcm(6, 8))
97 | assertEquals(975, lcm(39, 75))
98 | assertEquals(13384091, lcm(1357, 9863))
99 | assertEquals(1339310349, lcm(13579, 98631))
100 | assertEquals(2089830349, lcm(23579, 88631))
101 | assertEquals(2022222222, lcm(2, 1011111111))
102 | assertEquals(2022222222, lcm(1011111111, 2))
103 | }
104 |
105 | @Test
106 | @Tag("Easy")
107 | fun minDivisor() {
108 | assertEquals(2, minDivisor(2))
109 | assertEquals(3, minDivisor(75))
110 | assertEquals(5, minDivisor(75 / 3))
111 | assertEquals(97, minDivisor(97))
112 | assertEquals(7, minDivisor(49))
113 | assertEquals(17, minDivisor(8653))
114 | assertEquals(2124679 , minDivisor(2124679 ))
115 | assertEquals(1073676287, minDivisor(1073676287))
116 | assertEquals(Int.MAX_VALUE, minDivisor(Int.MAX_VALUE))
117 | }
118 |
119 | @Test
120 | @Tag("Easy")
121 | fun maxDivisor() {
122 | assertEquals(1, maxDivisor(17))
123 | assertEquals(12, maxDivisor(24))
124 | assertEquals(59, maxDivisor(177))
125 | assertEquals(17, maxDivisor(34))
126 | assertEquals(7, maxDivisor(49))
127 | assertEquals(509, maxDivisor(8653))
128 | assertEquals(1 , maxDivisor(2124679 ))
129 | assertEquals(1, maxDivisor(1073676287))
130 | assertEquals(1, maxDivisor(Int.MAX_VALUE))
131 | }
132 |
133 | @Test
134 | @Tag("Easy")
135 | fun isCoPrime() {
136 | assertTrue(isCoPrime(25, 49))
137 | assertFalse(isCoPrime(6, 8))
138 | assertTrue(isCoPrime(17, 97))
139 | assertFalse(isCoPrime(37, 111))
140 | assertTrue(isCoPrime(1234567890, 908765431))
141 | assertTrue(isCoPrime(2109876543, 1234567891))
142 | }
143 |
144 | @Test
145 | @Tag("Easy")
146 | fun squareBetweenExists() {
147 | assertTrue(squareBetweenExists(1, 1))
148 | assertTrue(squareBetweenExists(21, 28))
149 | assertTrue(squareBetweenExists(36, 48))
150 | assertTrue(squareBetweenExists(50, 64))
151 | assertFalse(squareBetweenExists(51, 61))
152 | assertFalse(squareBetweenExists(999, 1001))
153 | assertTrue(squareBetweenExists(152374337, 152423715))
154 | assertFalse(squareBetweenExists(2147395601, Int.MAX_VALUE))
155 | }
156 |
157 | @Test
158 | @Tag("Normal")
159 | fun collatzSteps() {
160 | assertEquals(0, collatzSteps(1))
161 | assertEquals(1, collatzSteps(2))
162 | assertEquals(7, collatzSteps(3))
163 | assertEquals(5, collatzSteps(5))
164 | assertEquals(6, collatzSteps(10))
165 | assertEquals(7, collatzSteps(20))
166 | assertEquals(6, collatzSteps(64))
167 | assertEquals(25, collatzSteps(100))
168 | assertEquals(7, collatzSteps(128))
169 | assertEquals(111, collatzSteps(1000))
170 | assertEquals(128, collatzSteps(100000))
171 | }
172 |
173 | @Test
174 | @Tag("Normal")
175 | fun sin() {
176 | assertEquals(0.0, sin(0.0, 1e-5), 1e-5)
177 | assertEquals(1.0, sin(PI / 2.0, 1e-5), 1e-5)
178 | assertEquals(0.0, sin(PI, 1e-5), 1e-5)
179 | assertEquals(-1.0, sin(3.0 * PI / 2.0, 1e-5), 1e-5)
180 | assertEquals(0.0, sin(100 * PI, 1e-5), 1e-5)
181 | }
182 |
183 | @Test
184 | @Tag("Normal")
185 | fun cos() {
186 | assertEquals(1.0, cos(0.0, 1e-5), 1e-5)
187 | assertEquals(0.0, cos(PI / 2.0, 1e-5), 1e-5)
188 | assertEquals(-1.0, cos(PI, 1e-5), 1e-5)
189 | assertEquals(0.0, cos(3.0 * PI / 2.0, 1e-5), 1e-5)
190 | assertEquals(1.0, cos(100 * PI, 1e-5), 1e-5)
191 | }
192 |
193 | @Test
194 | @Tag("Normal")
195 | fun revert() {
196 | assertEquals(87431, revert(13478))
197 | assertEquals(0, revert(0))
198 | assertEquals(3, revert(3))
199 | assertEquals(111, revert(111))
200 | assertEquals(17571, revert(17571))
201 | assertEquals(123456789, revert(987654321))
202 | }
203 |
204 | @Test
205 | @Tag("Normal")
206 | fun isPalindrome() {
207 | assertTrue(isPalindrome(3))
208 | assertFalse(isPalindrome(3653))
209 | assertTrue(isPalindrome(15751))
210 | assertTrue(isPalindrome(24688642))
211 | }
212 |
213 | @Test
214 | @Tag("Normal")
215 | fun hasDifferentDigits() {
216 | assertTrue(hasDifferentDigits(323))
217 | assertTrue(hasDifferentDigits(54))
218 | assertTrue(hasDifferentDigits(222266666))
219 | assertFalse(hasDifferentDigits(0))
220 | assertFalse(hasDifferentDigits(777))
221 | }
222 |
223 | @Test
224 | @Tag("Hard")
225 | fun squareSequenceDigit() {
226 | assertEquals(1, squareSequenceDigit(1))
227 | assertEquals(4, squareSequenceDigit(2))
228 | assertEquals(5, squareSequenceDigit(7))
229 | assertEquals(6, squareSequenceDigit(12))
230 | assertEquals(0, squareSequenceDigit(17))
231 | assertEquals(9, squareSequenceDigit(27))
232 | }
233 |
234 | @Test
235 | @Tag("Hard")
236 | fun fibSequenceDigit() {
237 | assertEquals(1, fibSequenceDigit(1))
238 | assertEquals(1, fibSequenceDigit(2))
239 | assertEquals(3, fibSequenceDigit(4))
240 | assertEquals(2, fibSequenceDigit(9))
241 | assertEquals(5, fibSequenceDigit(14))
242 | assertEquals(2, fibSequenceDigit(20))
243 | }
244 | }
--------------------------------------------------------------------------------
/src/lesson8/task2/Chess.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson8.task2
3 |
4 | /**
5 | * Клетка шахматной доски. Шахматная доска квадратная и имеет 8 х 8 клеток.
6 | * Поэтому, обе координаты клетки (горизонталь row, вертикаль column) могут находиться в пределах от 1 до 8.
7 | * Горизонтали нумеруются снизу вверх, вертикали слева направо.
8 | */
9 | data class Square(val column: Int, val row: Int) {
10 | /**
11 | * Пример
12 | *
13 | * Возвращает true, если клетка находится в пределах доски
14 | */
15 | fun inside(): Boolean = column in 1..8 && row in 1..8
16 |
17 | /**
18 | * Простая
19 | *
20 | * Возвращает строковую нотацию для клетки.
21 | * В нотации, колонки обозначаются латинскими буквами от a до h, а ряды -- цифрами от 1 до 8.
22 | * Для клетки не в пределах доски вернуть пустую строку
23 | */
24 | fun notation(): String = TODO()
25 | }
26 |
27 | /**
28 | * Простая
29 | *
30 | * Создаёт клетку по строковой нотации.
31 | * В нотации, колонки обозначаются латинскими буквами от a до h, а ряды -- цифрами от 1 до 8.
32 | * Если нотация некорректна, бросить IllegalArgumentException
33 | */
34 | fun square(notation: String): Square = TODO()
35 |
36 | /**
37 | * Простая
38 | *
39 | * Определить число ходов, за которое шахматная ладья пройдёт из клетки start в клетку end.
40 | * Шахматная ладья может за один ход переместиться на любую другую клетку
41 | * по вертикали или горизонтали.
42 | * Ниже точками выделены возможные ходы ладьи, а крестиками -- невозможные:
43 | *
44 | * xx.xxххх
45 | * xх.хxххх
46 | * ..Л.....
47 | * xх.хxххх
48 | * xx.xxххх
49 | * xx.xxххх
50 | * xx.xxххх
51 | * xx.xxххх
52 | *
53 | * Если клетки start и end совпадают, вернуть 0.
54 | * Если любая из клеток некорректна, бросить IllegalArgumentException().
55 | *
56 | * Пример: rookMoveNumber(Square(3, 1), Square(6, 3)) = 2
57 | * Ладья может пройти через клетку (3, 3) или через клетку (6, 1) к клетке (6, 3).
58 | */
59 | fun rookMoveNumber(start: Square, end: Square): Int = TODO()
60 |
61 | /**
62 | * Средняя
63 | *
64 | * Вернуть список из клеток, по которым шахматная ладья может быстрее всего попасть из клетки start в клетку end.
65 | * Описание ходов ладьи см. предыдущую задачу.
66 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start.
67 | * Между ними должны находиться промежуточные клетки, по порядку от start до end.
68 | * Примеры: rookTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3))
69 | * (здесь возможен ещё один вариант)
70 | * rookTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(3, 3), Square(6, 3))
71 | * (здесь возможен единственный вариант)
72 | * rookTrajectory(Square(3, 5), Square(8, 5)) = listOf(Square(3, 5), Square(8, 5))
73 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них.
74 | */
75 | fun rookTrajectory(start: Square, end: Square): List = TODO()
76 |
77 | /**
78 | * Простая
79 | *
80 | * Определить число ходов, за которое шахматный слон пройдёт из клетки start в клетку end.
81 | * Шахматный слон может за один ход переместиться на любую другую клетку по диагонали.
82 | * Ниже точками выделены возможные ходы слона, а крестиками -- невозможные:
83 | *
84 | * .xxx.ххх
85 | * x.x.xххх
86 | * xxСxxxxx
87 | * x.x.xххх
88 | * .xxx.ххх
89 | * xxxxx.хх
90 | * xxxxxх.х
91 | * xxxxxхх.
92 | *
93 | * Если клетки start и end совпадают, вернуть 0.
94 | * Если клетка end недостижима для слона, вернуть -1.
95 | * Если любая из клеток некорректна, бросить IllegalArgumentException().
96 | *
97 | * Примеры: bishopMoveNumber(Square(3, 1), Square(6, 3)) = -1; bishopMoveNumber(Square(3, 1), Square(3, 7)) = 2.
98 | * Слон может пройти через клетку (6, 4) к клетке (3, 7).
99 | */
100 | fun bishopMoveNumber(start: Square, end: Square): Int = TODO()
101 |
102 | /**
103 | * Сложная
104 | *
105 | * Вернуть список из клеток, по которым шахматный слон может быстрее всего попасть из клетки start в клетку end.
106 | * Описание ходов слона см. предыдущую задачу.
107 | *
108 | * Если клетка end недостижима для слона, вернуть пустой список.
109 | *
110 | * Если клетка достижима:
111 | * - список всегда включает в себя клетку start
112 | * - клетка end включается, если она не совпадает со start.
113 | * - между ними должны находиться промежуточные клетки, по порядку от start до end.
114 | *
115 | * Примеры: bishopTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3))
116 | * bishopTrajectory(Square(3, 1), Square(3, 7)) = listOf(Square(3, 1), Square(6, 4), Square(3, 7))
117 | * bishopTrajectory(Square(1, 3), Square(6, 8)) = listOf(Square(1, 3), Square(6, 8))
118 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них.
119 | */
120 | fun bishopTrajectory(start: Square, end: Square): List = TODO()
121 |
122 | /**
123 | * Средняя
124 | *
125 | * Определить число ходов, за которое шахматный король пройдёт из клетки start в клетку end.
126 | * Шахматный король одним ходом может переместиться из клетки, в которой стоит,
127 | * на любую соседнюю по вертикали, горизонтали или диагонали.
128 | * Ниже точками выделены возможные ходы короля, а крестиками -- невозможные:
129 | *
130 | * xxxxx
131 | * x...x
132 | * x.K.x
133 | * x...x
134 | * xxxxx
135 | *
136 | * Если клетки start и end совпадают, вернуть 0.
137 | * Если любая из клеток некорректна, бросить IllegalArgumentException().
138 | *
139 | * Пример: kingMoveNumber(Square(3, 1), Square(6, 3)) = 3.
140 | * Король может последовательно пройти через клетки (4, 2) и (5, 2) к клетке (6, 3).
141 | */
142 | fun kingMoveNumber(start: Square, end: Square): Int = TODO()
143 |
144 | /**
145 | * Сложная
146 | *
147 | * Вернуть список из клеток, по которым шахматный король может быстрее всего попасть из клетки start в клетку end.
148 | * Описание ходов короля см. предыдущую задачу.
149 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start.
150 | * Между ними должны находиться промежуточные клетки, по порядку от start до end.
151 | * Примеры: kingTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3))
152 | * (здесь возможны другие варианты)
153 | * kingTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(4, 2), Square(5, 2), Square(6, 3))
154 | * (здесь возможен единственный вариант)
155 | * kingTrajectory(Square(3, 5), Square(6, 2)) = listOf(Square(3, 5), Square(4, 4), Square(5, 3), Square(6, 2))
156 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них.
157 | */
158 | fun kingTrajectory(start: Square, end: Square): List = TODO()
159 |
160 | /**
161 | * Сложная
162 | *
163 | * Определить число ходов, за которое шахматный конь пройдёт из клетки start в клетку end.
164 | * Шахматный конь одним ходом вначале передвигается ровно на 2 клетки по горизонтали или вертикали,
165 | * а затем ещё на 1 клетку под прямым углом, образуя букву "Г".
166 | * Ниже точками выделены возможные ходы коня, а крестиками -- невозможные:
167 | *
168 | * .xxx.xxx
169 | * xxKxxxxx
170 | * .xxx.xxx
171 | * x.x.xxxx
172 | * xxxxxxxx
173 | * xxxxxxxx
174 | * xxxxxxxx
175 | * xxxxxxxx
176 | *
177 | * Если клетки start и end совпадают, вернуть 0.
178 | * Если любая из клеток некорректна, бросить IllegalArgumentException().
179 | *
180 | * Пример: knightMoveNumber(Square(3, 1), Square(6, 3)) = 3.
181 | * Конь может последовательно пройти через клетки (5, 2) и (4, 4) к клетке (6, 3).
182 | */
183 | fun knightMoveNumber(start: Square, end: Square): Int = TODO()
184 |
185 | /**
186 | * Очень сложная
187 | *
188 | * Вернуть список из клеток, по которым шахматный конь может быстрее всего попасть из клетки start в клетку end.
189 | * Описание ходов коня см. предыдущую задачу.
190 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start.
191 | * Между ними должны находиться промежуточные клетки, по порядку от start до end.
192 | * Примеры:
193 | *
194 | * knightTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3))
195 | * здесь возможны другие варианты)
196 | * knightTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(5, 2), Square(4, 4), Square(6, 3))
197 | * (здесь возможен единственный вариант)
198 | * knightTrajectory(Square(3, 5), Square(5, 6)) = listOf(Square(3, 5), Square(5, 6))
199 | * (здесь опять возможны другие варианты)
200 | * knightTrajectory(Square(7, 7), Square(8, 8)) =
201 | * listOf(Square(7, 7), Square(5, 8), Square(4, 6), Square(6, 7), Square(8, 8))
202 | *
203 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них.
204 | */
205 | fun knightTrajectory(start: Square, end: Square): List = TODO()
206 |
--------------------------------------------------------------------------------
/src/lesson6/task1/Parse.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER", "ConvertCallChainIntoSequence")
2 |
3 | package lesson6.task1
4 |
5 | /**
6 | * Пример
7 | *
8 | * Время представлено строкой вида "11:34:45", содержащей часы, минуты и секунды, разделённые двоеточием.
9 | * Разобрать эту строку и рассчитать количество секунд, прошедшее с начала дня.
10 | */
11 | fun timeStrToSeconds(str: String): Int {
12 | val parts = str.split(":")
13 | var result = 0
14 | for (part in parts) {
15 | val number = part.toInt()
16 | result = result * 60 + number
17 | }
18 | return result
19 | }
20 |
21 | /**
22 | * Пример
23 | *
24 | * Дано число n от 0 до 99.
25 | * Вернуть его же в виде двухсимвольной строки, от "00" до "99"
26 | */
27 | fun twoDigitStr(n: Int) = if (n in 0..9) "0$n" else "$n"
28 |
29 | /**
30 | * Пример
31 | *
32 | * Дано seconds -- время в секундах, прошедшее с начала дня.
33 | * Вернуть текущее время в виде строки в формате "ЧЧ:ММ:СС".
34 | */
35 | fun timeSecondsToStr(seconds: Int): String {
36 | val hour = seconds / 3600
37 | val minute = (seconds % 3600) / 60
38 | val second = seconds % 60
39 | return String.format("%02d:%02d:%02d", hour, minute, second)
40 | }
41 |
42 | /**
43 | * Пример: консольный ввод
44 | */
45 | fun main(args: Array) {
46 | println("Введите время в формате ЧЧ:ММ:СС")
47 | val line = readLine()
48 | if (line != null) {
49 | val seconds = timeStrToSeconds(line)
50 | if (seconds == -1) {
51 | println("Введённая строка $line не соответствует формату ЧЧ:ММ:СС")
52 | }
53 | else {
54 | println("Прошло секунд с начала суток: $seconds")
55 | }
56 | }
57 | else {
58 | println("Достигнут <конец файла> в процессе чтения строки. Программа прервана")
59 | }
60 | }
61 |
62 |
63 | /**
64 | * Средняя
65 | *
66 | * Дата представлена строкой вида "15 июля 2016".
67 | * Перевести её в цифровой формат "15.07.2016".
68 | * День и месяц всегда представлять двумя цифрами, например: 03.04.2011.
69 | * При неверном формате входной строки вернуть пустую строку.
70 | *
71 | * Обратите внимание: некорректная с точки зрения календаря дата (например, 30.02.2009) считается неверными
72 | * входными данными.
73 | */
74 | fun dateStrToDigit(str: String): String = TODO()
75 |
76 | /**
77 | * Средняя
78 | *
79 | * Дата представлена строкой вида "15.07.2016".
80 | * Перевести её в строковый формат вида "15 июля 2016".
81 | * При неверном формате входной строки вернуть пустую строку
82 | *
83 | * Обратите внимание: некорректная с точки зрения календаря дата (например, 30 февраля 2009) считается неверными
84 | * входными данными.
85 | */
86 | fun dateDigitToStr(digital: String): String = TODO()
87 |
88 | /**
89 | * Средняя
90 | *
91 | * Номер телефона задан строкой вида "+7 (921) 123-45-67".
92 | * Префикс (+7) может отсутствовать, код города (в скобках) также может отсутствовать.
93 | * Может присутствовать неограниченное количество пробелов и чёрточек,
94 | * например, номер 12 -- 34- 5 -- 67 -89 тоже следует считать легальным.
95 | * Перевести номер в формат без скобок, пробелов и чёрточек (но с +), например,
96 | * "+79211234567" или "123456789" для приведённых примеров.
97 | * Все символы в номере, кроме цифр, пробелов и +-(), считать недопустимыми.
98 | * При неверном формате вернуть пустую строку
99 | */
100 | fun flattenPhoneNumber(phone: String): String = TODO()
101 |
102 | /**
103 | * Средняя
104 | *
105 | * Результаты спортсмена на соревнованиях в прыжках в длину представлены строкой вида
106 | * "706 - % 717 % 703".
107 | * В строке могут присутствовать числа, черточки - и знаки процента %, разделённые пробелами;
108 | * число соответствует удачному прыжку, - пропущенной попытке, % заступу.
109 | * Прочитать строку и вернуть максимальное присутствующее в ней число (717 в примере).
110 | * При нарушении формата входной строки или при отсутствии в ней чисел, вернуть -1.
111 | */
112 | fun bestLongJump(jumps: String): Int = TODO()
113 |
114 | /**
115 | * Сложная
116 | *
117 | * Результаты спортсмена на соревнованиях в прыжках в высоту представлены строкой вида
118 | * "220 + 224 %+ 228 %- 230 + 232 %%- 234 %".
119 | * Здесь + соответствует удачной попытке, % неудачной, - пропущенной.
120 | * Высота и соответствующие ей попытки разделяются пробелом.
121 | * Прочитать строку и вернуть максимальную взятую высоту (230 в примере).
122 | * При нарушении формата входной строки вернуть -1.
123 | */
124 | fun bestHighJump(jumps: String): Int = TODO()
125 |
126 | /**
127 | * Сложная
128 | *
129 | * В строке представлено выражение вида "2 + 31 - 40 + 13",
130 | * использующее целые положительные числа, плюсы и минусы, разделённые пробелами.
131 | * Наличие двух знаков подряд "13 + + 10" или двух чисел подряд "1 2" не допускается.
132 | * Вернуть значение выражения (6 для примера).
133 | * Про нарушении формата входной строки бросить исключение IllegalArgumentException
134 | */
135 | fun plusMinus(expression: String): Int = TODO()
136 |
137 | /**
138 | * Сложная
139 | *
140 | * Строка состоит из набора слов, отделённых друг от друга одним пробелом.
141 | * Определить, имеются ли в строке повторяющиеся слова, идущие друг за другом.
142 | * Слова, отличающиеся только регистром, считать совпадающими.
143 | * Вернуть индекс начала первого повторяющегося слова, или -1, если повторов нет.
144 | * Пример: "Он пошёл в в школу" => результат 9 (индекс первого 'в')
145 | */
146 | fun firstDuplicateIndex(str: String): Int = TODO()
147 |
148 | /**
149 | * Сложная
150 | *
151 | * Строка содержит названия товаров и цены на них в формате вида
152 | * "Хлеб 39.9; Молоко 62; Курица 184.0; Конфеты 89.9".
153 | * То есть, название товара отделено от цены пробелом,
154 | * а цена отделена от названия следующего товара точкой с запятой и пробелом.
155 | * Вернуть название самого дорогого товара в списке (в примере это Курица),
156 | * или пустую строку при нарушении формата строки.
157 | * Все цены должны быть больше либо равны нуля.
158 | */
159 | fun mostExpensive(description: String): String = TODO()
160 |
161 | /**
162 | * Сложная
163 | *
164 | * Перевести число roman, заданное в римской системе счисления,
165 | * в десятичную систему и вернуть как результат.
166 | * Римские цифры: 1 = I, 4 = IV, 5 = V, 9 = IX, 10 = X, 40 = XL, 50 = L,
167 | * 90 = XC, 100 = C, 400 = CD, 500 = D, 900 = CM, 1000 = M.
168 | * Например: XXIII = 23, XLIV = 44, C = 100
169 | *
170 | * Вернуть -1, если roman не является корректным римским числом
171 | */
172 | fun fromRoman(roman: String): Int = TODO()
173 |
174 | /**
175 | * Очень сложная
176 | *
177 | * Имеется специальное устройство, представляющее собой
178 | * конвейер из cells ячеек (нумеруются от 0 до cells - 1 слева направо) и датчик, двигающийся над этим конвейером.
179 | * Строка commands содержит последовательность команд, выполняемых данным устройством, например +>+>+>+>+
180 | * Каждая команда кодируется одним специальным символом:
181 | * > - сдвиг датчика вправо на 1 ячейку;
182 | * < - сдвиг датчика влево на 1 ячейку;
183 | * + - увеличение значения в ячейке под датчиком на 1 ед.;
184 | * - - уменьшение значения в ячейке под датчиком на 1 ед.;
185 | * [ - если значение под датчиком равно 0, в качестве следующей команды следует воспринимать
186 | * не следующую по порядку, а идущую за соответствующей следующей командой ']' (с учётом вложенности);
187 | * ] - если значение под датчиком не равно 0, в качестве следующей команды следует воспринимать
188 | * не следующую по порядку, а идущую за соответствующей предыдущей командой '[' (с учётом вложенности);
189 | * (комбинация [] имитирует цикл)
190 | * пробел - пустая команда
191 | *
192 | * Изначально все ячейки заполнены значением 0 и датчик стоит на ячейке с номером N/2 (округлять вниз)
193 | *
194 | * После выполнения limit команд или всех команд из commands следует прекратить выполнение последовательности команд.
195 | * Учитываются все команды, в том числе несостоявшиеся переходы ("[" при значении под датчиком не равном 0 и "]" при
196 | * значении под датчиком равном 0) и пробелы.
197 | *
198 | * Вернуть список размера cells, содержащий элементы ячеек устройства после завершения выполнения последовательности.
199 | * Например, для 10 ячеек и командной строки +>+>+>+>+ результат должен быть 0,0,0,0,0,1,1,1,1,1
200 | *
201 | * Все прочие символы следует считать ошибочными и формировать исключение IllegalArgumentException.
202 | * То же исключение формируется, если у символов [ ] не оказывается пары.
203 | * Выход за границу конвейера также следует считать ошибкой и формировать исключение IllegalStateException.
204 | * Считать, что ошибочные символы и непарные скобки являются более приоритетной ошибкой чем выход за границу ленты,
205 | * то есть если в программе присутствует некорректный символ или непарная скобка, то должно быть выброшено
206 | * IllegalArgumentException.
207 | * IllegalArgumentException должен бросаться даже если ошибочная команда не была достигнута в ходе выполнения.
208 | *
209 | */
210 | fun computeDeviceCells(cells: Int, commands: String, limit: Int): List = TODO()
211 |
--------------------------------------------------------------------------------
/test/lesson4/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson4.task1
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 |
7 | class Tests {
8 | @Test
9 | @Tag("Example")
10 | fun sqRoots() {
11 | assertEquals(listOf(), sqRoots(-1.0))
12 | assertArrayEquals(listOf(0.0).toDoubleArray(), sqRoots(0.0).toDoubleArray(), 1e-5)
13 | assertArrayEquals(listOf(-5.0, 5.0).toDoubleArray(), sqRoots(25.0).toDoubleArray(), 1e-5)
14 | }
15 |
16 | @Test
17 | @Tag("Example")
18 | fun biRoots() {
19 | assertEquals(listOf(), biRoots(0.0, 0.0, 1.0))
20 | assertEquals(listOf(), biRoots(0.0, 1.0, 2.0))
21 | assertArrayEquals(
22 | listOf(-2.0, 2.0).toDoubleArray(),
23 | biRoots(0.0, 1.0, -4.0).toDoubleArray(),
24 | 1e-5)
25 | assertEquals(listOf(), biRoots(1.0, -2.0, 4.0))
26 | assertArrayEquals(
27 | listOf(-1.0, 1.0).toDoubleArray(),
28 | biRoots(1.0, -2.0, 1.0).toDoubleArray(),
29 | 1e-5)
30 | assertEquals(listOf(), biRoots(1.0, 3.0, 2.0))
31 | assertArrayEquals(
32 | listOf(-2.0, -1.0, 1.0, 2.0).toDoubleArray(),
33 | biRoots(1.0, -5.0, 4.0).sorted().toDoubleArray(),
34 | 1e-5)
35 | }
36 |
37 | @Test
38 | @Tag("Example")
39 | fun negativeList() {
40 | assertEquals(listOf(), negativeList(listOf(1, 2, 3)))
41 | assertEquals(listOf(-1, -5), negativeList(listOf(-1, 2, 4, -5)))
42 | }
43 |
44 | @Test
45 | @Tag("Example")
46 | fun invertPositives() {
47 | val list1 = mutableListOf(1, 2, 3)
48 | invertPositives(list1)
49 | assertEquals(listOf(-1, -2, -3), list1)
50 | val list2 = mutableListOf(-1, 2, 4, -5)
51 | invertPositives(list2)
52 | assertEquals(listOf(-1, -2, -4, -5), list2)
53 | }
54 |
55 | @Test
56 | @Tag("Example")
57 | fun squares() {
58 | assertEquals(listOf(0), squares(listOf(0)))
59 | assertEquals(listOf(1, 4, 9), squares(listOf(1, 2, -3)))
60 | }
61 |
62 | @Test
63 | @Tag("Example")
64 | fun squaresVararg() {
65 | assertArrayEquals(arrayOf(0), squares(0))
66 | assertArrayEquals(arrayOf(1, 4, 9), squares(1, 2, -3))
67 | }
68 |
69 | @Test
70 | @Tag("Example")
71 | fun isPalindrome() {
72 | assertFalse(isPalindrome("Барабан"))
73 | assertTrue(isPalindrome("А роза упала на лапу Азора"))
74 | assertTrue(isPalindrome("Шалаш"))
75 | }
76 |
77 | @Test
78 | @Tag("Example")
79 | fun buildSumExample() {
80 | assertEquals("42 = 42", buildSumExample(listOf(42)))
81 | assertEquals("3 + 6 + 5 + 4 + 9 = 27", buildSumExample(listOf(3, 6, 5, 4, 9)))
82 | }
83 |
84 | @Test
85 | @Tag("Easy")
86 | fun abs() {
87 | assertEquals(0.0, abs(listOf()), 1e-5)
88 | assertEquals(3.0, abs(listOf(3.0)), 1e-5)
89 | assertEquals(5.0, abs(listOf(3.0, -4.0)), 1e-5)
90 | assertEquals(8.774964, abs(listOf(4.0, -5.0, 6.0)), 1e-5)
91 | }
92 |
93 | @Test
94 | @Tag("Easy")
95 | fun mean() {
96 | assertEquals(0.0, mean(listOf()), 1e-5)
97 | assertEquals(1.0, mean(listOf(1.0)), 1e-5)
98 | assertEquals(2.0, mean(listOf(3.0, 1.0, 2.0)), 1e-5)
99 | assertEquals(3.0, mean(listOf(0.0, 2.0, 7.0, 8.0, -2.0)), 1e-5)
100 | }
101 |
102 | @Test
103 | @Tag("Normal")
104 | fun center() {
105 | assertEquals(listOf(), center(mutableListOf()))
106 | assertArrayEquals(
107 | listOf(0.0).toDoubleArray(),
108 | center(mutableListOf(3.14)).toDoubleArray(),
109 | 1e-5)
110 | assertArrayEquals(
111 | listOf(1.0, -1.0, 0.0).toDoubleArray(),
112 | center(mutableListOf(3.0, 1.0, 2.0)).toDoubleArray(),
113 | 1e-5)
114 | assertArrayEquals(
115 | listOf(-3.0, -1.0, 4.0, 5.0, -5.0).toDoubleArray(),
116 | center(mutableListOf(0.0, 2.0, 7.0, 8.0, -2.0)).toDoubleArray(),
117 | 1e-5)
118 | val toMutate = mutableListOf(-3.0, -1.0, 4.0, 5.0, -5.0)
119 | assertTrue(toMutate === center(toMutate)) { "You should mutate an input list, not create a copy" }
120 | }
121 |
122 | @Test
123 | @Tag("Normal")
124 | fun times() {
125 | assertEquals(0.0, times(listOf(), listOf()), 1e-5)
126 | assertEquals(-5.0, times(listOf(1.0, -4.0), listOf(3.0, 2.0)), 1e-5)
127 | assertEquals(-19.0, times(listOf(-1.0, 2.0, -3.0), listOf(3.0, -2.0, 4.0)), 1e-5)
128 | }
129 |
130 | @Test
131 | @Tag("Normal")
132 | fun polynom() {
133 | assertEquals(0.0, polynom(listOf(), 1000.0), 1e-5)
134 | assertEquals(42.0, polynom(listOf(42.0), -1000.0), 1e-5)
135 | assertEquals(13.0, polynom(listOf(3.0, 2.0), 5.0), 1e-5)
136 | assertEquals(0.0, polynom(listOf(2.0, -3.0, 1.0), 1.0), 1e-5)
137 | assertEquals(45.0, polynom(listOf(-7.0, 6.0, 4.0, -4.0, 1.0), -2.0), 1e-5)
138 | }
139 |
140 | @Test
141 | @Tag("Normal")
142 | fun accumulate() {
143 | assertEquals(listOf(), accumulate(arrayListOf()))
144 | assertArrayEquals(
145 | listOf(3.14).toDoubleArray(),
146 | accumulate(arrayListOf(3.14)).toDoubleArray(),
147 | 1e-5)
148 | assertArrayEquals(
149 | listOf(1.0, 3.0, 6.0, 10.0).toDoubleArray(),
150 | accumulate(arrayListOf(1.0, 2.0, 3.0, 4.0)).toDoubleArray(),
151 | 1e-5)
152 | val toMutate = mutableListOf(-3.0, -1.0, 4.0, 5.0, -5.0)
153 | assertTrue(toMutate === accumulate(toMutate)) { "You should mutate an input list, not create a copy" }
154 | }
155 |
156 | @Test
157 | @Tag("Normal")
158 | fun factorize() {
159 | assertEquals(listOf(2), factorize(2))
160 | assertEquals(listOf(3, 5, 5), factorize(75))
161 | assertEquals(listOf(2, 3, 3, 19), factorize(342))
162 | }
163 |
164 | @Test
165 | @Tag("Hard")
166 | fun factorizeToString() {
167 | assertEquals("2", factorizeToString(2))
168 | assertEquals("3*5*5", factorizeToString(75))
169 | assertEquals("2*3*3*19", factorizeToString(342))
170 | assertEquals("7*7*31*31*151*151", factorizeToString(1073676289))
171 | assertEquals("1073676287", factorizeToString(1073676287))
172 | assertEquals(Int.MAX_VALUE.toString(), factorizeToString(Int.MAX_VALUE))
173 | }
174 |
175 | @Test
176 | @Tag("Normal")
177 | fun convert() {
178 | assertEquals(listOf(1), convert(1, 2))
179 | assertEquals(listOf(1, 2, 1, 0), convert(100, 4))
180 | assertEquals(listOf(1, 3, 12), convert(250, 14))
181 | assertEquals(listOf(2, 14, 12), convert(1000, 19))
182 | }
183 |
184 | @Test
185 | @Tag("Hard")
186 | fun convertToString() {
187 | assertEquals("1", convertToString(1, 2))
188 | assertEquals("1210", convertToString(100, 4))
189 | assertEquals("13c", convertToString(250, 14))
190 | assertEquals("2ec", convertToString(1000, 19))
191 | assertEquals("z", convertToString(35, 36))
192 | assertEquals("a02220281", convertToString(Int.MAX_VALUE, 11))
193 | }
194 |
195 | @Test
196 | @Tag("Normal")
197 | fun decimal() {
198 | assertEquals(1, decimal(listOf(1), 2))
199 | assertEquals(100, decimal(listOf(1, 2, 1, 0), 4))
200 | assertEquals(250, decimal(listOf(1, 3, 12), 14))
201 | assertEquals(1000, decimal(listOf(2, 14, 12), 19))
202 | }
203 |
204 | @Test
205 | @Tag("Hard")
206 | fun decimalFromString() {
207 | assertEquals(1, decimalFromString("1", 2))
208 | assertEquals(100, decimalFromString("1210", 4))
209 | assertEquals(250, decimalFromString("13c", 14))
210 | assertEquals(1000, decimalFromString("2ec", 19))
211 | assertEquals(35, decimalFromString("z", 36))
212 | assertEquals(Int.MAX_VALUE, decimalFromString("a02220281", 11))
213 | }
214 |
215 | @Test
216 | @Tag("Hard")
217 | fun roman() {
218 | assertEquals("I", roman(1))
219 | assertEquals("MMM", roman(3000))
220 | assertEquals("MCMLXXVIII", roman(1978))
221 | assertEquals("DCXCIV", roman(694))
222 | assertEquals("XLIX", roman(49))
223 | }
224 |
225 | @Test
226 | @Tag("Impossible")
227 | fun russian() {
228 | assertEquals("триста семьдесят пять", russian(375))
229 | assertEquals("двадцать две тысячи девятьсот шестьдесят четыре", russian(22964))
230 | assertEquals("сто девятнадцать тысяч пятьсот восемь", russian(119508))
231 | assertEquals("две тысячи три", russian(2003))
232 | assertEquals("двести тысяч два", russian(200002))
233 | assertEquals("девятьсот тысяч", russian(900000))
234 | assertEquals("двенадцать", russian(12))
235 | }
236 | }
--------------------------------------------------------------------------------
/test/lesson8/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson8.task1
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 | import java.lang.Math.ulp
7 | import kotlin.math.PI
8 | import kotlin.math.abs
9 | import kotlin.math.sqrt
10 |
11 | class Tests {
12 | @Test
13 | @Tag("Example")
14 | fun pointDistance() {
15 | assertEquals(0.0, Point(0.0, 0.0).distance(Point(0.0, 0.0)), 1e-5)
16 | assertEquals(5.0, Point(3.0, 0.0).distance(Point(0.0, 4.0)), 1e-5)
17 | assertEquals(50.0, Point(0.0, -30.0).distance(Point(-40.0, 0.0)), 1e-5)
18 | }
19 |
20 | @Test
21 | @Tag("Example")
22 | fun halfPerimeter() {
23 | assertEquals(6.0, Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).halfPerimeter(), 1e-5)
24 | assertEquals(2.0, Triangle(Point(0.0, 0.0), Point(0.0, 1.0), Point(0.0, 2.0)).halfPerimeter(), 1e-5)
25 | }
26 |
27 | @Test
28 | @Tag("Example")
29 | fun triangleArea() {
30 | assertEquals(6.0, Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).area(), 1e-5)
31 | assertEquals(0.0, Triangle(Point(0.0, 0.0), Point(0.0, 1.0), Point(0.0, 2.0)).area(), 1e-5)
32 | }
33 |
34 | @Test
35 | @Tag("Example")
36 | fun triangleContains() {
37 | assertTrue(Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).contains(Point(1.5, 1.5)))
38 | assertFalse(Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).contains(Point(2.5, 2.5)))
39 | }
40 |
41 | @Test
42 | @Tag("Example")
43 | fun segmentEquals() {
44 | val first = Segment(Point(1.0, 2.0), Point(3.0, 4.0))
45 | val second = Segment(Point(1.0, 2.0), Point(3.0, 4.0))
46 | val third = Segment(Point(3.0, 4.0), Point(1.0, 2.0))
47 | assertEquals(first, second)
48 | assertEquals(second, third)
49 | assertEquals(third, first)
50 | }
51 |
52 | private fun approxEquals(expected: Line, actual: Line, delta: Double): Boolean =
53 | abs(expected.angle - actual.angle) <= delta &&
54 | abs(expected.b - actual.b) <= delta
55 |
56 | private fun assertApproxEquals(expected: Line, actual: Line, delta: Double = ulp(10.0)) {
57 | assertTrue(approxEquals(expected, actual, delta))
58 | }
59 |
60 | private fun assertApproxNotEquals(expected: Line, actual: Line, delta: Double = ulp(10.0)) {
61 | assertFalse(approxEquals(expected, actual, delta))
62 | }
63 |
64 | @Test
65 | @Tag("Example")
66 | fun lineEquals() {
67 | run {
68 | val first = Line(Point(0.0, 0.0), 0.0)
69 | val second = Line(Point(3.0, 0.0), 0.0)
70 | val third = Line(Point(-5.0, 0.0), 0.0)
71 | val fourth = Line(Point(3.0, 1.0), 0.0)
72 | assertApproxEquals(first, second)
73 | assertApproxEquals(second, third)
74 | assertApproxEquals(third, first)
75 | assertApproxNotEquals(fourth, first)
76 | }
77 | run {
78 | val first = Line(Point(0.0, 0.0), PI / 2)
79 | val second = Line(Point(0.0, 3.0), PI / 2)
80 | val third = Line(Point(0.0, -5.0), PI / 2)
81 | val fourth = Line(Point(1.0, 3.0), PI / 2)
82 | assertApproxEquals(first, second)
83 | assertApproxEquals(second, third)
84 | assertApproxEquals(third, first)
85 | assertApproxNotEquals(fourth, first)
86 | }
87 | run {
88 | val first = Line(Point(0.0, 0.0), PI / 4)
89 | val second = Line(Point(3.0, 3.0), PI / 4)
90 | val third = Line(Point(-5.0, -5.0), PI / 4)
91 | val fourth = Line(Point(3.00001, 3.0), PI / 4)
92 | assertApproxEquals(first, second)
93 | assertApproxEquals(second, third)
94 | assertApproxEquals(third, first)
95 | assertApproxNotEquals(fourth, first)
96 | }
97 | }
98 |
99 | @Test
100 | @Tag("Example")
101 | fun triangleEquals() {
102 | val first = Triangle(Point(0.0, 0.0), Point(3.0, 0.0), Point(0.0, 4.0))
103 | val second = Triangle(Point(0.0, 0.0), Point(0.0, 4.0), Point(3.0, 0.0))
104 | val third = Triangle(Point(0.0, 4.0), Point(0.0, 0.0), Point(3.0, 0.0))
105 | val fourth = Triangle(Point(0.0, 4.0), Point(0.0, 3.0), Point(3.0, 0.0))
106 | assertEquals(first, second)
107 | assertEquals(second, third)
108 | assertEquals(third, first)
109 | assertNotEquals(fourth, first)
110 | }
111 |
112 | @Test
113 | @Tag("Easy")
114 | fun circleDistance() {
115 | assertEquals(0.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(1.0, 0.0), 1.0)), 1e-5)
116 | assertEquals(0.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(0.0, 2.0), 1.0)), 1e-5)
117 | assertEquals(1.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(-4.0, 0.0), 2.0)), 1e-5)
118 | assertEquals(2.0 * sqrt(2.0) - 2.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(2.0, 2.0), 1.0)), 1e-5)
119 | }
120 |
121 | @Test
122 | @Tag("Trivial")
123 | fun circleContains() {
124 | val center = Point(1.0, 2.0)
125 | assertTrue(Circle(center, 1.0).contains(center))
126 | assertFalse(Circle(center, 2.0).contains(Point(0.0, 0.0)))
127 | assertTrue(Circle(Point(0.0, 3.0), 5.01).contains(Point(-4.0, 0.0)))
128 | }
129 |
130 | @Test
131 | @Tag("Normal")
132 | fun diameter() {
133 | val p1 = Point(0.0, 0.0)
134 | val p2 = Point(1.0, 4.0)
135 | val p3 = Point(-2.0, 2.0)
136 | val p4 = Point(3.0, -1.0)
137 | val p5 = Point(-3.0, -2.0)
138 | val p6 = Point(0.0, 5.0)
139 | assertEquals(Segment(p5, p6), diameter(p1, p2, p3, p4, p5, p6))
140 | assertEquals(Segment(p4, p6), diameter(p1, p2, p3, p4, p6))
141 | assertEquals(Segment(p3, p4), diameter(p1, p2, p3, p4))
142 | assertEquals(Segment(p2, p4), diameter(p1, p2, p4))
143 | assertEquals(Segment(p1, p4), diameter(p1, p4))
144 | }
145 |
146 | @Test
147 | @Tag("Easy")
148 | fun circleByDiameter() {
149 | assertEquals(Circle(Point(0.0, 1.0), 1.0), circleByDiameter(Segment(Point(0.0, 0.0), Point(0.0, 2.0))))
150 | assertEquals(Circle(Point(2.0, 1.5), 2.5), circleByDiameter(Segment(Point(4.0, 0.0), Point(0.0, 3.0))))
151 | }
152 |
153 | @Test
154 | @Tag("Normal")
155 | fun crossPoint() {
156 | assertTrue(Point(2.0, 3.0).distance(Line(Point(2.0, 0.0), PI / 2).crossPoint(Line(Point(0.0, 3.0), 0.0))) < 1e-5)
157 | assertTrue(Point(2.0, 2.0).distance(Line(Point(0.0, 0.0), PI / 4).crossPoint(Line(Point(0.0, 4.0), 3 * PI / 4))) < 1e-5)
158 | val p = Point(1.0, 3.0)
159 | assertTrue(p.distance(Line(p, 1.0).crossPoint(Line(p, 2.0))) < 1e-5)
160 | }
161 |
162 | @Test
163 | @Tag("Normal")
164 | fun lineBySegment() {
165 | assertApproxEquals(Line(Point(0.0, 0.0), 0.0), lineBySegment(Segment(Point(0.0, 0.0), Point(7.0, 0.0))))
166 | assertApproxEquals(Line(Point(0.0, 0.0), PI / 2), lineBySegment(Segment(Point(0.0, 0.0), Point(0.0, 8.0))))
167 | assertApproxEquals(Line(Point(1.0, 1.0), PI / 4), lineBySegment(Segment(Point(1.0, 1.0), Point(3.0, 3.0))))
168 | }
169 |
170 | @Test
171 | @Tag("Normal")
172 | fun lineByPoint() {
173 | assertApproxEquals(Line(Point(0.0, 0.0), PI / 2), lineByPoints(Point(0.0, 0.0), Point(0.0, 2.0)))
174 | assertApproxEquals(Line(Point(1.0, 1.0), PI / 4), lineByPoints(Point(1.0, 1.0), Point(3.0, 3.0)))
175 | }
176 |
177 | @Test
178 | @Tag("Hard")
179 | fun bisectorByPoints() {
180 | assertApproxEquals(Line(Point(2.0, 0.0), PI / 2), bisectorByPoints(Point(0.0, 0.0), Point(4.0, 0.0)))
181 | assertApproxEquals(Line(Point(1.0, 2.0), 0.0), bisectorByPoints(Point(1.0, 5.0), Point(1.0, -1.0)))
182 | }
183 |
184 | @Test
185 | @Tag("Normal")
186 | fun findNearestCirclePair() {
187 | val c1 = Circle(Point(0.0, 0.0), 1.0)
188 | val c2 = Circle(Point(3.0, 0.0), 5.0)
189 | val c3 = Circle(Point(-5.0, 0.0), 2.0)
190 | val c4 = Circle(Point(0.0, 7.0), 3.0)
191 | val c5 = Circle(Point(0.0, -6.0), 4.0)
192 | assertEquals(Pair(c1, c5), findNearestCirclePair(c1, c3, c4, c5))
193 | assertEquals(Pair(c2, c4), findNearestCirclePair(c2, c4, c5))
194 | assertEquals(Pair(c1, c2), findNearestCirclePair(c1, c2, c4, c5))
195 | }
196 |
197 | @Test
198 | @Tag("Hard")
199 | fun circleByThreePoints() {
200 | val result = circleByThreePoints(Point(5.0, 0.0), Point(3.0, 4.0), Point(0.0, -5.0))
201 | assertTrue(result.center.distance(Point(0.0, 0.0)) < 1e-5)
202 | assertEquals(5.0, result.radius, 1e-5)
203 | }
204 |
205 | @Test
206 | @Tag("Impossible")
207 | fun minContainingCircle() {
208 | val p1 = Point(0.0, 0.0)
209 | val p2 = Point(1.0, 4.0)
210 | val p3 = Point(-2.0, 2.0)
211 | val p4 = Point(3.0, -1.0)
212 | val p5 = Point(-3.0, -2.0)
213 | val p6 = Point(0.0, 5.0)
214 | val result = minContainingCircle(p1, p2, p3, p4, p5, p6)
215 | assertEquals(4.0, result.radius, 0.02)
216 | for (p in listOf(p1, p2, p3, p4, p5, p6)) {
217 | assertTrue(result.contains(p))
218 | }
219 | }
220 | }
--------------------------------------------------------------------------------
/src/lesson5/task1/Map.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER", "ConvertCallChainIntoSequence")
2 |
3 | package lesson5.task1
4 |
5 | /**
6 | * Пример
7 | *
8 | * Для заданного списка покупок `shoppingList` посчитать его общую стоимость
9 | * на основе цен из `costs`. В случае неизвестной цены считать, что товар
10 | * игнорируется.
11 | */
12 | fun shoppingListCost(
13 | shoppingList: List,
14 | costs: Map): Double {
15 | var totalCost = 0.0
16 |
17 | for (item in shoppingList) {
18 | val itemCost = costs[item]
19 | if (itemCost != null) {
20 | totalCost += itemCost
21 | }
22 | }
23 |
24 | return totalCost
25 | }
26 |
27 | /**
28 | * Пример
29 | *
30 | * Для набора "имя"-"номер телефона" `phoneBook` оставить только такие пары,
31 | * для которых телефон начинается с заданного кода страны `countryCode`
32 | */
33 | fun filterByCountryCode(
34 | phoneBook: MutableMap,
35 | countryCode: String) {
36 | val namesToRemove = mutableListOf()
37 |
38 | for ((name, phone) in phoneBook) {
39 | if (!phone.startsWith(countryCode)) {
40 | namesToRemove.add(name)
41 | }
42 | }
43 |
44 | for (name in namesToRemove) {
45 | phoneBook.remove(name)
46 | }
47 | }
48 |
49 | /**
50 | * Пример
51 | *
52 | * Для заданного текста `text` убрать заданные слова-паразиты `fillerWords`
53 | * и вернуть отфильтрованный текст
54 | */
55 | fun removeFillerWords(
56 | text: List,
57 | vararg fillerWords: String): List {
58 | val fillerWordSet = setOf(*fillerWords)
59 |
60 | val res = mutableListOf()
61 | for (word in text) {
62 | if (word !in fillerWordSet) {
63 | res += word
64 | }
65 | }
66 | return res
67 | }
68 |
69 | /**
70 | * Пример
71 | *
72 | * Для заданного текста `text` построить множество встречающихся в нем слов
73 | */
74 | fun buildWordSet(text: List): MutableSet {
75 | val res = mutableSetOf()
76 | for (word in text) res.add(word)
77 | return res
78 | }
79 |
80 | /**
81 | * Средняя
82 | *
83 | * Объединить два ассоциативных массива `mapA` и `mapB` с парами
84 | * "имя"-"номер телефона" в итоговый ассоциативный массив, склеивая
85 | * значения для повторяющихся ключей через запятую.
86 | * В случае повторяющихся *ключей* значение из mapA должно быть
87 | * перед значением из mapB.
88 | *
89 | * Повторяющиеся *значения* следует добавлять только один раз.
90 | *
91 | * Например:
92 | * mergePhoneBooks(
93 | * mapOf("Emergency" to "112", "Police" to "02"),
94 | * mapOf("Emergency" to "911", "Police" to "02")
95 | * ) -> mapOf("Emergency" to "112, 911", "Police" to "02")
96 | */
97 | fun mergePhoneBooks(mapA: Map, mapB: Map): Map = TODO()
98 |
99 | /**
100 | * Простая
101 | *
102 | * По заданному ассоциативному массиву "студент"-"оценка за экзамен" построить
103 | * обратный массив "оценка за экзамен"-"список студентов с этой оценкой".
104 | *
105 | * Например:
106 | * buildGrades(mapOf("Марат" to 3, "Семён" to 5, "Михаил" to 5))
107 | * -> mapOf(5 to listOf("Семён", "Михаил"), 3 to listOf("Марат"))
108 | */
109 | fun buildGrades(grades: Map): Map> = TODO()
110 |
111 | /**
112 | * Простая
113 | *
114 | * Определить, входит ли ассоциативный массив a в ассоциативный массив b;
115 | * это выполняется, если все ключи из a содержатся в b с такими же значениями.
116 | *
117 | * Например:
118 | * containsIn(mapOf("a" to "z"), mapOf("a" to "z", "b" to "sweet")) -> true
119 | * containsIn(mapOf("a" to "z"), mapOf("a" to "zee", "b" to "sweet")) -> false
120 | */
121 | fun containsIn(a: Map, b: Map): Boolean = TODO()
122 |
123 | /**
124 | * Средняя
125 | *
126 | * Для заданного списка пар "акция"-"стоимость" вернуть ассоциативный массив,
127 | * содержащий для каждой акции ее усредненную стоимость.
128 | *
129 | * Например:
130 | * averageStockPrice(listOf("MSFT" to 100.0, "MSFT" to 200.0, "NFLX" to 40.0))
131 | * -> mapOf("MSFT" to 150.0, "NFLX" to 40.0)
132 | */
133 | fun averageStockPrice(stockPrices: List>): Map = TODO()
134 |
135 | /**
136 | * Средняя
137 | *
138 | * Входными данными является ассоциативный массив
139 | * "название товара"-"пара (тип товара, цена товара)"
140 | * и тип интересующего нас товара.
141 | * Необходимо вернуть название товара заданного типа с минимальной стоимостью
142 | * или null в случае, если товаров такого типа нет.
143 | *
144 | * Например:
145 | * findCheapestStuff(
146 | * mapOf("Мария" to ("печенье" to 20.0), "Орео" to ("печенье" to 100.0)),
147 | * "печенье"
148 | * ) -> "Мария"
149 | */
150 | fun findCheapestStuff(stuff: Map>, kind: String): String? = TODO()
151 |
152 | /**
153 | * Сложная
154 | *
155 | * Для заданного ассоциативного массива знакомых через одно рукопожатие `friends`
156 | * необходимо построить его максимальное расширение по рукопожатиям, то есть,
157 | * для каждого человека найти всех людей, с которыми он знаком через любое
158 | * количество рукопожатий.
159 | * Считать, что все имена людей являются уникальными, а также что рукопожатия
160 | * являются направленными, то есть, если Марат знает Свету, то это не означает,
161 | * что Света знает Марата.
162 | *
163 | * Например:
164 | * propagateHandshakes(
165 | * mapOf(
166 | * "Marat" to setOf("Mikhail", "Sveta"),
167 | * "Sveta" to setOf("Marat"),
168 | * "Mikhail" to setOf("Sveta")
169 | * )
170 | * ) -> mapOf(
171 | * "Marat" to setOf("Mikhail", "Sveta"),
172 | * "Sveta" to setOf("Marat", "Mikhail"),
173 | * "Mikhail" to setOf("Sveta", "Marat")
174 | * )
175 | */
176 | fun propagateHandshakes(friends: Map>): Map> = TODO()
177 |
178 | /**
179 | * Простая
180 | *
181 | * Удалить из изменяемого ассоциативного массива все записи,
182 | * которые встречаются в заданном ассоциативном массиве.
183 | * Записи считать одинаковыми, если и ключи, и значения совпадают.
184 | *
185 | * ВАЖНО: необходимо изменить переданный в качестве аргумента
186 | * изменяемый ассоциативный массив
187 | *
188 | * Например:
189 | * subtractOf(a = mutableMapOf("a" to "z"), mapOf("a" to "z"))
190 | * -> a changes to mutableMapOf() aka becomes empty
191 | */
192 | fun subtractOf(a: MutableMap, b: Map): Unit = TODO()
193 |
194 | /**
195 | * Простая
196 | *
197 | * Для двух списков людей найти людей, встречающихся в обоих списках.
198 | * В выходном списке не должно быть повторяюихся элементов,
199 | * т. е. whoAreInBoth(listOf("Марат", "Семён, "Марат"), listOf("Марат", "Марат")) == listOf("Марат")
200 | */
201 | fun whoAreInBoth(a: List, b: List): List = TODO()
202 |
203 | /**
204 | * Средняя
205 | *
206 | * Для заданного набора символов определить, можно ли составить из него
207 | * указанное слово (регистр символов игнорируется)
208 | *
209 | * Например:
210 | * canBuildFrom(listOf('a', 'b', 'o'), "baobab") -> true
211 | */
212 | fun canBuildFrom(chars: List, word: String): Boolean = TODO()
213 |
214 | /**
215 | * Средняя
216 | *
217 | * Найти в заданном списке повторяющиеся элементы и вернуть
218 | * ассоциативный массив с информацией о числе повторений
219 | * для каждого повторяющегося элемента.
220 | * Если элемент встречается только один раз, включать его в результат
221 | * не следует.
222 | *
223 | * Например:
224 | * extractRepeats(listOf("a", "b", "a")) -> mapOf("a" to 2)
225 | */
226 | fun extractRepeats(list: List): Map = TODO()
227 |
228 | /**
229 | * Средняя
230 | *
231 | * Для заданного списка слов определить, содержит ли он анаграммы
232 | * (два слова являются анаграммами, если одно можно составить из второго)
233 | *
234 | * Например:
235 | * hasAnagrams(listOf("тор", "свет", "рот")) -> true
236 | */
237 | fun hasAnagrams(words: List): Boolean = TODO()
238 |
239 | /**
240 | * Сложная
241 | *
242 | * Для заданного списка неотрицательных чисел и числа определить,
243 | * есть ли в списке пара чисел таких, что их сумма равна заданному числу.
244 | * Если да, верните их индексы в виде Pair;
245 | * если нет, верните пару Pair(-1, -1).
246 | *
247 | * Индексы в результате должны следовать в порядке (меньший, больший).
248 | *
249 | * Постарайтесь сделать ваше решение как можно более эффективным,
250 | * используя то, что вы узнали в данном уроке.
251 | *
252 | * Например:
253 | * findSumOfTwo(listOf(1, 2, 3), 4) -> Pair(0, 2)
254 | * findSumOfTwo(listOf(1, 2, 3), 6) -> Pair(-1, -1)
255 | */
256 | fun findSumOfTwo(list: List, number: Int): Pair = TODO()
257 |
258 | /**
259 | * Очень сложная
260 | *
261 | * Входными данными является ассоциативный массив
262 | * "название сокровища"-"пара (вес сокровища, цена сокровища)"
263 | * и вместимость вашего рюкзака.
264 | * Необходимо вернуть множество сокровищ с максимальной суммарной стоимостью,
265 | * которые вы можете унести в рюкзаке.
266 | *
267 | * Например:
268 | * bagPacking(
269 | * mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)),
270 | * 850
271 | * ) -> setOf("Кубок")
272 | * bagPacking(
273 | * mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)),
274 | * 450
275 | * ) -> emptySet()
276 | */
277 | fun bagPacking(treasures: Map>, capacity: Int): Set = TODO()
278 |
--------------------------------------------------------------------------------
/tutorial/chapter06_5.adoc:
--------------------------------------------------------------------------------
1 | = 6.5. Доктор RegExp
2 |
3 | __Регулярные выражения (RegExp)__ -- специальный язык для описания множества строк.
4 | Они помогают решать задачу поиска какого-либо текста (из описанного множества) в другом тексте,
5 | описывают интересующий нас текст и работают достаточно эффективно для **быстрого** решения задачи поиска.
6 |
7 | В некоторых случаях количество вариантов искомого текста настолько велико, что перечислять все варианты
8 | становится неудобно.
9 | Иногда все эти варианты могут быть представлены одной строкой -- __регулярным выражением__.
10 |
11 | Примеры регулярных выражений (см. слайды):
12 |
13 | * ```KotlinAsFirst```
14 | * ```[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}```
15 | * ```ˆ4[0-9]{12}(?:[0-9]{3})?$```
16 | * ```[-+]?[0-9]*\.?[0-9]+```
17 | * ```<([a-z]+)([ˆ<]+)*(?:>(.*)<\/\1>|\s+\/>)```
18 |
19 | Поиск регулярного выражения осуществляется с помощью автомата с состояниями, или __конечного автомата__.
20 | В данном случае под этим понимается алгоритм, имеющий некоторое количество устойчивых __состояний__.
21 | Для каждого состояния определяются действия, которые алгоритм выполняет в этом состоянии,
22 | а также условия, по которым алгоритм переходит в другие состояния.
23 |
24 | == Возможности языка регулярных выражений
25 |
26 | Регулярное выражение в общем случае -- это строка, в которой часть символов играет специальную роль.
27 | Но большинство символов в регулярном выражении обозначают просто самих себя. Например:
28 |
29 | * `KotlinAsFirst`
30 | * `Трансмогрификация`
31 | * `Мама мыла раму`
32 | * `42`
33 |
34 | Существует однако ряд специальных символов.
35 | Например, __класс символов__ обозначает любой символ из определённого множества:
36 |
37 | * ```[0123456789]``` -- любая цифра
38 | * ```[aeiouy]``` -- любая буква из перечисленных
39 | * ```[~!@#$%^&*+-]``` -- любой символ из перечисленных
40 |
41 | Отрицание класса символов ищет любой символ НЕ из заданного множества:
42 |
43 | * ```[^0123456789]``` -- всё, что угодно, кроме цифры
44 | * ```[^a-z]``` -- всё, что угодно, кроме строчной латинской буквы
45 | * ```[^-az]``` -- всё, что угодно, кроме `-`, `a`, `z`
46 |
47 | Классы и их отрицания, как видим, используют специальные символы `[...]` для обозначения класса,
48 | специальный символ `-` для обозначения интервала символов и
49 | последовательность `[^...]` для обозначения отрицания.
50 |
51 | __Якоря__ позволяют найти начало или конец всей строки:
52 |
53 | * ```^fun``` -- `fun` в начале строки
54 | * ```\.$``` -- точка в конце строки
55 | * ```^Kotlin is great as the first language!$``` -- ВСЯ строка с заданной фразой (и более ничем)
56 |
57 | Здесь `^` используется для обозначения начала строки, а `$` для обозначения конца.
58 | Следует иметь в виду, что якоря никак не учитывают переводы строк --
59 | имеется в виду начало или конец всего текста, а не одной строки в тексте.
60 |
61 | `\.` использует экранирование для обозначения символа `.`,
62 | поскольку в регулярных выражениях точка является специальным символом (и обозначает любой символ).
63 | Таким образом, `\` в регулярных выражениях экранирует последующий символ,
64 | делая его из специального символа обыкновенным.
65 | Для обозначения символа `\` применяется пара `\\`.
66 | Аналогично, `\^` обозначает символ-шапку, `\$` -- символ доллара, `\[` -- открывающую квадратную скобку,
67 | `\]` -- закрывающую квадратную скобку.
68 |
69 | Особые символы ищут символы по специальным правилам:
70 |
71 | * ``` ..... ``` -- любая последовательность из пяти символов, начинающаяся и заканчивающаяся пробелов
72 | * `\t` -- табуляция, `\n` -- новая строка, `\r` -- возврат каретки (два последних символа унаследованы компьютерами от эпохи пишущих машинок, когда для начала печати с новой строки необходимо было выполнить два действия -- __возврат каретки__ в начало строки и перевод каретки на __новую строку__)
73 | * `\s` -- произвольный вид пробела (пробел, табуляция, новая строка, возврат каретки)
74 | * `\d` -- произвольная цифра, аналог ``[0-9]``
75 | * `\w` -- произвольная "символ в слове", обычно аналог ``[a-zA-z0-9]``, то есть, латинская буква или цифра
76 | * `\S` -- НЕ пробел, `\D` -- НЕ цифра, `\W` -- НЕ "символ в слове"
77 |
78 | __Шаблон выбора__ `|` ищет одну строку из нескольких, например:
79 |
80 | * ```Марат|Михаил``` -- Марат или Михаил
81 | * ```^\[|\]$``` -- открывающая квадратная скобка в начале строки или закрывающая в конце
82 | * ```for.*(val|var).*``` -- цикл `for` с последующим `val` или `var`
83 |
84 | __Шаблоны количества__ ищут определённое число совпадений:
85 |
86 | * ```.*``` -- любое количество (в том числе ноль) любых символов
87 | * ```(Марат)+``` -- строка Марат один или более раз (но не ноль)
88 | * ```(Михаил)?``` -- строка Михаил ноль или один раз
89 | * ```([0-9]{4})``` -- последовательность из ровно четырёх любых цифр
90 | * ```\w{8,16}``` -- последовательность из 8-16 "символов в слове"
91 |
92 | Круглые скобки `()` задают так называемые __группы поиска__, объединяя несколько символов вместе.
93 |
94 | * ```(Kotlin)+AsFirst``` -- KotlinAsFirst, KotlinKotlinAsFirst, KotlinKotlinKotlinAsFirst, ...
95 | * ```(?:\$\$)+``` -- `$$`, `$$$$`, `$$$$$$`, ...
96 | * ```(\w+)\s\1``` -- слово, за которым следует пробел и то же самое слово.
97 | * ```fun\s+(/w+)\s*\{.*\1.*\}``` -- `fun` с последующими пробелами, произвольным словом в круглых скобках, пробелами и тем же словом в фигурных скобках
98 |
99 | Здесь `\1` (`\2`, `\3`, ...) ищет **уже описанную** группу поиска по её номеру внутри регулярного выражения
100 | (в данном случае -- первую группу).
101 | Комбинация `(?:...)` задаёт группу поиска **без номера**.
102 | В целом, `(?...)` задаёт __группы особого поиска__:
103 |
104 | * ```Марат(?=\sАхин)``` -- Марат, за которым следует пробел и Ахин
105 | * ```(?<=Михаил\s)Глухих``` -- Глухих, перед которым стоит Михаил с пробелом
106 | * ```\d+(?![$\d])``` -- число, после которого НЕ стоит знак доллара
107 | * ```(? previous * 60 + next
157 | }
158 | }
159 | ----
160 |
161 | Здесь мы разбираем исходную строку вида "12:34:56" с целью найти в ней три одинаковых группы поиска `(\d\d)`.
162 | Каждая из групп поиска включает в себя две цифры.
163 | Убедившись с помощью проверки на **null**, что регулярное выражение успешно найдено,
164 | мы отбрасываем первый элемент `groupValues` с помощью функции `drop(1)`,
165 | оставляя, таким образом, в списке только значения трёх групп поиска.
166 | Далее каждая из пар цифр конвертируется в число.
167 | Результат сворачивается в число секунд, прошедших с начала дня, с помощью функции высшего порядка `fold` -- см. раздел 4.
168 |
169 | == Полезные ссылки
170 |
171 | * http://regexr.com
172 | * https://regex101.com
173 | * https://docs.oracle.com/javase/tutorial/essential/regex
--------------------------------------------------------------------------------
/src/lesson9/task2/Matrices.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_PARAMETER")
2 | package lesson9.task2
3 |
4 | import lesson9.task1.Matrix
5 | import lesson9.task1.createMatrix
6 |
7 | // Все задачи в этом файле требуют наличия реализации интерфейса "Матрица" в Matrix.kt
8 |
9 | /**
10 | * Пример
11 | *
12 | * Транспонировать заданную матрицу matrix.
13 | * При транспонировании строки матрицы становятся столбцами и наоборот:
14 | *
15 | * 1 2 3 1 4 6 3
16 | * 4 5 6 ==> 2 5 5 2
17 | * 6 5 4 3 6 4 1
18 | * 3 2 1
19 | */
20 | fun transpose(matrix: Matrix): Matrix {
21 | if (matrix.width < 1 || matrix.height < 1) return matrix
22 | val result = createMatrix(height = matrix.width, width = matrix.height, e = matrix[0, 0])
23 | for (i in 0 until matrix.width) {
24 | for (j in 0 until matrix.height) {
25 | result[i, j] = matrix[j, i]
26 | }
27 | }
28 | return result
29 | }
30 |
31 | /**
32 | * Пример
33 | *
34 | * Сложить две заданные матрицы друг с другом.
35 | * Складывать можно только матрицы совпадающего размера -- в противном случае бросить IllegalArgumentException.
36 | * При сложении попарно складываются соответствующие элементы матриц
37 | */
38 | operator fun Matrix.plus(other: Matrix): Matrix {
39 | if (width != other.width || height != other.height) throw IllegalArgumentException()
40 | if (width < 1 || height < 1) return this
41 | val result = createMatrix(height, width, this[0, 0])
42 | for (i in 0 until height) {
43 | for (j in 0 until width) {
44 | result[i, j] = this[i, j] + other[i, j]
45 | }
46 | }
47 | return result
48 | }
49 |
50 | /**
51 | * Сложная
52 | *
53 | * Заполнить матрицу заданной высоты height и ширины width
54 | * натуральными числами от 1 до m*n по спирали,
55 | * начинающейся в левом верхнем углу и закрученной по часовой стрелке.
56 | *
57 | * Пример для height = 3, width = 4:
58 | * 1 2 3 4
59 | * 10 11 12 5
60 | * 9 8 7 6
61 | */
62 | fun generateSpiral(height: Int, width: Int): Matrix = TODO()
63 |
64 | /**
65 | * Сложная
66 | *
67 | * Заполнить матрицу заданной высоты height и ширины width следующим образом.
68 | * Элементам, находящимся на периферии (по периметру матрицы), присвоить значение 1;
69 | * периметру оставшейся подматрицы – значение 2 и так далее до заполнения всей матрицы.
70 | *
71 | * Пример для height = 5, width = 6:
72 | * 1 1 1 1 1 1
73 | * 1 2 2 2 2 1
74 | * 1 2 3 3 2 1
75 | * 1 2 2 2 2 1
76 | * 1 1 1 1 1 1
77 | */
78 | fun generateRectangles(height: Int, width: Int): Matrix = TODO()
79 |
80 | /**
81 | * Сложная
82 | *
83 | * Заполнить матрицу заданной высоты height и ширины width диагональной змейкой:
84 | * в левый верхний угол 1, во вторую от угла диагональ 2 и 3 сверху вниз, в третью 4-6 сверху вниз и так далее.
85 | *
86 | * Пример для height = 5, width = 4:
87 | * 1 2 4 7
88 | * 3 5 8 11
89 | * 6 9 12 15
90 | * 10 13 16 18
91 | * 14 17 19 20
92 | */
93 | fun generateSnake(height: Int, width: Int): Matrix = TODO()
94 |
95 | /**
96 | * Средняя
97 | *
98 | * Содержимое квадратной матрицы matrix (с произвольным содержимым) повернуть на 90 градусов по часовой стрелке.
99 | * Если height != width, бросить IllegalArgumentException.
100 | *
101 | * Пример: Станет:
102 | * 1 2 3 7 4 1
103 | * 4 5 6 8 5 2
104 | * 7 8 9 9 6 3
105 | */
106 | fun rotate(matrix: Matrix): Matrix = TODO()
107 |
108 | /**
109 | * Сложная
110 | *
111 | * Проверить, является ли квадратная целочисленная матрица matrix латинским квадратом.
112 | * Латинским квадратом называется матрица размером n x n,
113 | * каждая строка и каждый столбец которой содержат все числа от 1 до n.
114 | * Если height != width, вернуть false.
115 | *
116 | * Пример латинского квадрата 3х3:
117 | * 2 3 1
118 | * 1 2 3
119 | * 3 1 2
120 | */
121 | fun isLatinSquare(matrix: Matrix): Boolean = TODO()
122 |
123 | /**
124 | * Средняя
125 | *
126 | * В матрице matrix каждый элемент заменить суммой непосредственно примыкающих к нему
127 | * элементов по вертикали, горизонтали и диагоналям.
128 | *
129 | * Пример для матрицы 4 x 3: (11=2+4+5, 19=1+3+4+5+6, ...)
130 | * 1 2 3 11 19 13
131 | * 4 5 6 ===> 19 31 19
132 | * 6 5 4 19 31 19
133 | * 3 2 1 13 19 11
134 | *
135 | * Поскольку в матрице 1 х 1 примыкающие элементы отсутствуют,
136 | * для неё следует вернуть как результат нулевую матрицу:
137 | *
138 | * 42 ===> 0
139 | */
140 | fun sumNeighbours(matrix: Matrix): Matrix = TODO()
141 |
142 | /**
143 | * Средняя
144 | *
145 | * Целочисленная матрица matrix состоит из "дырок" (на их месте стоит 0) и "кирпичей" (на их месте стоит 1).
146 | * Найти в этой матрице все ряды и колонки, целиком состоящие из "дырок".
147 | * Результат вернуть в виде Holes(rows = список дырчатых рядов, columns = список дырчатых колонок).
148 | * Ряды и колонки нумеруются с нуля. Любой из спискоов rows / columns может оказаться пустым.
149 | *
150 | * Пример для матрицы 5 х 4:
151 | * 1 0 1 0
152 | * 0 0 1 0
153 | * 1 0 0 0 ==> результат: Holes(rows = listOf(4), columns = listOf(1, 3)): 4-й ряд, 1-я и 3-я колонки
154 | * 0 0 1 0
155 | * 0 0 0 0
156 | */
157 | fun findHoles(matrix: Matrix): Holes = TODO()
158 |
159 | /**
160 | * Класс для описания местонахождения "дырок" в матрице
161 | */
162 | data class Holes(val rows: List, val columns: List)
163 |
164 | /**
165 | * Средняя
166 | *
167 | * В целочисленной матрице matrix каждый элемент заменить суммой элементов подматрицы,
168 | * расположенной в левом верхнем углу матрицы matrix и ограниченной справа-снизу данным элементом.
169 | *
170 | * Пример для матрицы 3 х 3:
171 | *
172 | * 1 2 3 1 3 6
173 | * 4 5 6 => 5 12 21
174 | * 7 8 9 12 27 45
175 | *
176 | * К примеру, центральный элемент 12 = 1 + 2 + 4 + 5, элемент в левом нижнем углу 12 = 1 + 4 + 7 и так далее.
177 | */
178 | fun sumSubMatrix(matrix: Matrix): Matrix = TODO()
179 |
180 | /**
181 | * Сложная
182 | *
183 | * Даны мозаичные изображения замочной скважины и ключа. Пройдет ли ключ в скважину?
184 | * То есть даны две матрицы key и lock, key.height <= lock.height, key.width <= lock.width, состоящие из нулей и единиц.
185 | *
186 | * Проверить, можно ли наложить матрицу key на матрицу lock (без поворота, разрешается только сдвиг) так,
187 | * чтобы каждой единице в матрице lock (штырь) соответствовал ноль в матрице key (прорезь),
188 | * а каждому нулю в матрице lock (дырка) соответствовала, наоборот, единица в матрице key (штырь).
189 | * Ключ при сдвиге не может выходить за пределы замка.
190 | *
191 | * Пример: ключ подойдёт, если его сдвинуть на 1 по ширине
192 | * lock key
193 | * 1 0 1 1 0
194 | * 0 1 0 0 1
195 | * 1 1 1
196 | *
197 | * Вернуть тройку (Triple) -- (да/нет, требуемый сдвиг по высоте, требуемый сдвиг по ширине).
198 | * Если наложение невозможно, то первый элемент тройки "нет" и сдвиги могут быть любыми.
199 | */
200 | fun canOpenLock(key: Matrix, lock: Matrix): Triple = TODO()
201 |
202 | /**
203 | * Простая
204 | *
205 | * Инвертировать заданную матрицу.
206 | * При инвертировании знак каждого элемента матрицы следует заменить на обратный
207 | */
208 | operator fun Matrix.unaryMinus(): Matrix = TODO(this.toString())
209 |
210 | /**
211 | * Средняя
212 | *
213 | * Перемножить две заданные матрицы друг с другом.
214 | * Матрицы можно умножать, только если ширина первой матрицы совпадает с высотой второй матрицы.
215 | * В противном случае бросить IllegalArgumentException.
216 | * Подробно про порядок умножения см. статью Википедии "Умножение матриц".
217 | */
218 | operator fun Matrix.times(other: Matrix): Matrix = TODO(this.toString())
219 |
220 | /**
221 | * Сложная
222 | *
223 | * В матрице matrix размером 4х4 дана исходная позиция для игры в 15, например
224 | * 5 7 9 1
225 | * 2 12 14 15
226 | * 3 4 6 8
227 | * 10 11 13 0
228 | *
229 | * Здесь 0 обозначает пустую клетку, а 1-15 – фишки с соответствующими номерами.
230 | * Напомним, что "игра в 15" имеет квадратное поле 4х4, по которому двигается 15 фишек,
231 | * одна клетка всегда остаётся пустой. Цель игры -- упорядочить фишки на игровом поле.
232 | *
233 | * В списке moves задана последовательность ходов, например [8, 6, 13, 11, 10, 3].
234 | * Ход задаётся номером фишки, которая передвигается на пустое место (то есть, меняется местами с нулём).
235 | * Фишка должна примыкать к пустому месту по горизонтали или вертикали, иначе ход не будет возможным.
236 | * Все номера должны быть в пределах от 1 до 15.
237 | * Определить финальную позицию после выполнения всех ходов и вернуть её.
238 | * Если какой-либо ход является невозможным или список содержит неверные номера,
239 | * бросить IllegalStateException.
240 | *
241 | * В данном случае должно получиться
242 | * 5 7 9 1
243 | * 2 12 14 15
244 | * 0 4 13 6
245 | * 3 10 11 8
246 | */
247 | fun fifteenGameMoves(matrix: Matrix, moves: List): Matrix = TODO()
248 |
249 | /**
250 | * Очень сложная
251 | *
252 | * В матрице matrix размером 4х4 дана исходная позиция для игры в 15, например
253 | * 5 7 9 2
254 | * 1 12 14 15
255 | * 3 4 6 8
256 | * 10 11 13 0
257 | *
258 | * Здесь 0 обозначает пустую клетку, а 1-15 – фишки с соответствующими номерами.
259 | * Напомним, что "игра в 15" имеет квадратное поле 4х4, по которому двигается 15 фишек,
260 | * одна клетка всегда остаётся пустой.
261 | *
262 | * Цель игры -- упорядочить фишки на игровом поле, приведя позицию к одному из следующих двух состояний:
263 | *
264 | * 1 2 3 4 1 2 3 4
265 | * 5 6 7 8 ИЛИ 5 6 7 8
266 | * 9 10 11 12 9 10 11 12
267 | * 13 14 15 0 13 15 14 0
268 | *
269 | * Можно математически доказать, что РОВНО ОДНО из этих двух состояний достижимо из любой исходной позиции.
270 | *
271 | * Вернуть решение -- список ходов, приводящих исходную позицию к одной из двух упорядоченных.
272 | * Каждый ход -- это перемена мест фишки с заданным номером с пустой клеткой (0),
273 | * при этом заданная фишка должна по горизонтали или по вертикали примыкать к пустой клетке (но НЕ по диагонали).
274 | * К примеру, ход 13 в исходной позиции меняет местами 13 и 0, а ход 11 в той же позиции невозможен.
275 | *
276 | * Одно из решений исходной позиции:
277 | *
278 | * [8, 6, 14, 12, 4, 11, 13, 14, 12, 4,
279 | * 7, 5, 1, 3, 11, 7, 3, 11, 7, 12, 6,
280 | * 15, 4, 9, 2, 4, 9, 3, 5, 2, 3, 9,
281 | * 15, 8, 14, 13, 12, 7, 11, 5, 7, 6,
282 | * 9, 15, 8, 14, 13, 9, 15, 7, 6, 12,
283 | * 9, 13, 14, 15, 12, 11, 10, 9, 13, 14,
284 | * 15, 12, 11, 10, 9, 13, 14, 15]
285 | *
286 | * Перед решением этой задачи НЕОБХОДИМО решить предыдущую
287 | */
288 | fun fifteenGameSolution(matrix: Matrix): List = TODO()
289 |
--------------------------------------------------------------------------------
/test/lesson5/task1/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson5.task1
2 |
3 | import org.junit.jupiter.api.Assertions.*
4 | import org.junit.jupiter.api.Tag
5 | import org.junit.jupiter.api.Test
6 |
7 | class Tests {
8 | @Test
9 | @Tag("Example")
10 | fun shoppingListCostTest() {
11 | val itemCosts = mapOf(
12 | "Хлеб" to 50.0,
13 | "Молоко" to 100.0
14 | )
15 | assertEquals(
16 | 150.0,
17 | shoppingListCost(
18 | listOf("Хлеб", "Молоко"),
19 | itemCosts
20 | )
21 | )
22 | assertEquals(
23 | 150.0,
24 | shoppingListCost(
25 | listOf("Хлеб", "Молоко", "Кефир"),
26 | itemCosts
27 | )
28 | )
29 | assertEquals(
30 | 0.0,
31 | shoppingListCost(
32 | listOf("Хлеб", "Молоко", "Кефир"),
33 | mapOf()
34 | )
35 | )
36 | }
37 |
38 | @Test
39 | @Tag("Example")
40 | fun filterByCountryCode() {
41 | val phoneBook = mutableMapOf(
42 | "Quagmire" to "+1-800-555-0143",
43 | "Adam's Ribs" to "+82-000-555-2960",
44 | "Pharmakon Industries" to "+1-800-555-6321"
45 | )
46 |
47 | filterByCountryCode(phoneBook, "+1")
48 | assertEquals(2, phoneBook.size)
49 |
50 | filterByCountryCode(phoneBook, "+1")
51 | assertEquals(2, phoneBook.size)
52 |
53 | filterByCountryCode(phoneBook, "+999")
54 | assertEquals(0, phoneBook.size)
55 | }
56 |
57 | @Test
58 | @Tag("Example")
59 | fun removeFillerWords() {
60 | assertEquals(
61 | "Я люблю Котлин".split(" "),
62 | removeFillerWords(
63 | "Я как-то люблю Котлин".split(" "),
64 | "как-то"
65 | )
66 | )
67 | assertEquals(
68 | "Я люблю Котлин".split(" "),
69 | removeFillerWords(
70 | "Я как-то люблю таки Котлин".split(" "),
71 | "как-то",
72 | "таки"
73 | )
74 | )
75 | assertEquals(
76 | "Я люблю Котлин".split(" "),
77 | removeFillerWords(
78 | "Я люблю Котлин".split(" "),
79 | "как-то",
80 | "таки"
81 | )
82 | )
83 | }
84 |
85 | @Test
86 | @Tag("Example")
87 | fun buildWordSet() {
88 | assertEquals(
89 | buildWordSet("Я люблю Котлин".split(" ")),
90 | mutableSetOf("Я", "люблю", "Котлин")
91 | )
92 | assertEquals(
93 | buildWordSet("Я люблю люблю Котлин".split(" ")),
94 | mutableSetOf("Котлин", "люблю", "Я")
95 | )
96 | assertEquals(
97 | buildWordSet(listOf()),
98 | mutableSetOf()
99 | )
100 | }
101 |
102 | @Test
103 | @Tag("Normal")
104 | fun mergePhoneBooks() {
105 | assertEquals(
106 | mapOf("Emergency" to "112"),
107 | mergePhoneBooks(
108 | mapOf("Emergency" to "112"),
109 | mapOf("Emergency" to "112")
110 | )
111 | )
112 | assertEquals(
113 | mapOf("Emergency" to "112", "Police" to "02"),
114 | mergePhoneBooks(
115 | mapOf("Emergency" to "112"),
116 | mapOf("Emergency" to "112", "Police" to "02")
117 | )
118 | )
119 | assertEquals(
120 | mapOf("Emergency" to "112, 911", "Police" to "02"),
121 | mergePhoneBooks(
122 | mapOf("Emergency" to "112"),
123 | mapOf("Emergency" to "911", "Police" to "02")
124 | )
125 | )
126 | assertEquals(
127 | mapOf("Emergency" to "112, 911", "Fire department" to "01", "Police" to "02"),
128 | mergePhoneBooks(
129 | mapOf("Emergency" to "112", "Fire department" to "01"),
130 | mapOf("Emergency" to "911", "Police" to "02")
131 | )
132 | )
133 | }
134 |
135 | @Test
136 | @Tag("Easy")
137 | fun buildGrades() {
138 | assertEquals(
139 | mapOf>(),
140 | buildGrades(mapOf())
141 | )
142 | assertEquals(
143 | mapOf(5 to listOf("Михаил", "Семён"), 3 to listOf("Марат")),
144 | buildGrades(mapOf("Марат" to 3, "Семён" to 5, "Михаил" to 5))
145 | .mapValues { (_, v) -> v.sorted() }
146 | )
147 | assertEquals(
148 | mapOf(3 to listOf("Марат", "Михаил", "Семён")),
149 | buildGrades(mapOf("Марат" to 3, "Семён" to 3, "Михаил" to 3))
150 | .mapValues { (_, v) -> v.sorted() }
151 | )
152 | }
153 |
154 | @Test
155 | @Tag("Easy")
156 | fun containsIn() {
157 | assertTrue(containsIn(mapOf("a" to "z"), mapOf("a" to "z", "b" to "sweet")))
158 | assertFalse(containsIn(mapOf("a" to "z"), mapOf("a" to "zee", "b" to "sweet")))
159 | }
160 |
161 | @Test
162 | @Tag("Normal")
163 | fun averageStockPrice() {
164 | assertEquals(
165 | mapOf(),
166 | averageStockPrice(listOf())
167 | )
168 | assertEquals(
169 | mapOf("MSFT" to 100.0, "NFLX" to 40.0),
170 | averageStockPrice(listOf("MSFT" to 100.0, "NFLX" to 40.0))
171 | )
172 | assertEquals(
173 | mapOf("MSFT" to 150.0, "NFLX" to 40.0),
174 | averageStockPrice(listOf("MSFT" to 100.0, "MSFT" to 200.0, "NFLX" to 40.0))
175 | )
176 | assertEquals(
177 | mapOf("MSFT" to 150.0, "NFLX" to 45.0),
178 | averageStockPrice(listOf("MSFT" to 100.0, "MSFT" to 200.0, "NFLX" to 40.0, "NFLX" to 50.0))
179 | )
180 | }
181 |
182 | @Test
183 | @Tag("Normal")
184 | fun findCheapestStuff() {
185 | assertNull(
186 | findCheapestStuff(
187 | mapOf("Мария" to ("печенье" to 20.0), "Орео" to ("печенье" to 100.0)),
188 | "торт"
189 | )
190 | )
191 | assertEquals(
192 | "Мария",
193 | findCheapestStuff(
194 | mapOf("Мария" to ("печенье" to 20.0), "Орео" to ("печенье" to 100.0)),
195 | "печенье"
196 | )
197 | )
198 | }
199 |
200 | @Test
201 | @Tag("Hard")
202 | fun propagateHandshakes() {
203 | assertEquals(
204 | mapOf(
205 | "Marat" to setOf("Mikhail", "Sveta"),
206 | "Sveta" to setOf("Mikhail"),
207 | "Mikhail" to setOf()
208 | ),
209 | propagateHandshakes(
210 | mapOf(
211 | "Marat" to setOf("Sveta"),
212 | "Sveta" to setOf("Mikhail")
213 | )
214 | )
215 | )
216 | assertEquals(
217 | mapOf(
218 | "Marat" to setOf("Mikhail", "Sveta"),
219 | "Sveta" to setOf("Marat", "Mikhail"),
220 | "Mikhail" to setOf("Sveta", "Marat")
221 | ),
222 | propagateHandshakes(
223 | mapOf(
224 | "Marat" to setOf("Mikhail", "Sveta"),
225 | "Sveta" to setOf("Marat"),
226 | "Mikhail" to setOf("Sveta")
227 | )
228 | )
229 | )
230 | }
231 |
232 | @Test
233 | @Tag("Easy")
234 | fun subtractOf() {
235 | val from = mutableMapOf("a" to "z", "b" to "c")
236 |
237 | subtractOf(from, mapOf())
238 | assertEquals(from, mapOf("a" to "z", "b" to "c"))
239 |
240 | subtractOf(from, mapOf("b" to "z"))
241 | assertEquals(from, mapOf("a" to "z", "b" to "c"))
242 |
243 | subtractOf(from, mapOf("a" to "z"))
244 | assertEquals(from, mapOf("b" to "c"))
245 | }
246 |
247 | @Test
248 | @Tag("Easy")
249 | fun whoAreInBoth() {
250 | assertEquals(
251 | emptyList(),
252 | whoAreInBoth(emptyList(), emptyList())
253 | )
254 | assertEquals(
255 | listOf("Marat"),
256 | whoAreInBoth(listOf("Marat", "Mikhail"), listOf("Marat", "Kirill"))
257 | )
258 | assertEquals(
259 | emptyList(),
260 | whoAreInBoth(listOf("Marat", "Mikhail"), listOf("Sveta", "Kirill"))
261 | )
262 | }
263 |
264 | @Test
265 | @Tag("Normal")
266 | fun canBuildFrom() {
267 | assertFalse(canBuildFrom(emptyList(), "foo"))
268 | assertTrue(canBuildFrom(listOf('a', 'b', 'o'), "baobab"))
269 | assertFalse(canBuildFrom(listOf('a', 'm', 'r'), "Marat"))
270 | }
271 |
272 | @Test
273 | @Tag("Normal")
274 | fun extractRepeats() {
275 | assertEquals(
276 | emptyMap(),
277 | extractRepeats(emptyList())
278 | )
279 | assertEquals(
280 | mapOf("a" to 2),
281 | extractRepeats(listOf("a", "b", "a"))
282 | )
283 | assertEquals(
284 | emptyMap(),
285 | extractRepeats(listOf("a", "b", "c"))
286 | )
287 | }
288 |
289 | @Test
290 | @Tag("Normal")
291 | fun hasAnagrams() {
292 | assertFalse(hasAnagrams(emptyList()))
293 | assertTrue(hasAnagrams(listOf("рот", "свет", "тор")))
294 | assertFalse(hasAnagrams(listOf("рот", "свет", "код", "дверь")))
295 | }
296 |
297 | @Test
298 | @Tag("Hard")
299 | fun findSumOfTwo() {
300 | assertEquals(
301 | Pair(-1, -1),
302 | findSumOfTwo(emptyList(), 1)
303 | )
304 | assertEquals(
305 | Pair(0, 2),
306 | findSumOfTwo(listOf(1, 2, 3), 4)
307 | )
308 | assertEquals(
309 | Pair(-1, -1),
310 | findSumOfTwo(listOf(1, 2, 3), 6)
311 | )
312 | }
313 |
314 | @Test
315 | @Tag("Impossible")
316 | fun bagPacking() {
317 | assertEquals(
318 | setOf("Кубок"),
319 | bagPacking(
320 | mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)),
321 | 850
322 | )
323 | )
324 | assertEquals(
325 | emptySet(),
326 | bagPacking(
327 | mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)),
328 | 450
329 | )
330 | )
331 | }
332 |
333 | // TODO: map task tests
334 | }
335 |
--------------------------------------------------------------------------------
/tutorial/chapter00.adoc:
--------------------------------------------------------------------------------
1 | = Введение
2 |
3 | Языки программирования -- интереснейшая область современной техники.
4 | За последние 30-40 лет информационные технологии разрослись до невероятных пределов, и сейчас мало кто в состоянии обозреть эту область в полном объёме.
5 | Компьютерные программы выросли с нескольких сотен строк до десятков миллионов строк, применяются сейчас в самых разных областях и запускаются на самых разных __платформах__, например:
6 |
7 | * обыкновенные программы для персонального компьютера, часто называемые desktop-программами;
8 | * web-программы, которые делятся, в свою очередь, на __клиентскую__ часть, выполняющуюся на компьютере пользователя, и __серверную__, выполняющуюся на сервере;
9 | * __мобильные__ программы для планшетов, смартфонов и других мобильных устройств;
10 | * __системные__ программы, являющиеся частью операционной системы;
11 | * __встраиваемые__ программы, являющиеся частью встраиваемых систем управления (применяемые, например, в транспорте, банкоматах, станках с программным управлением, при программировании роботов).
12 |
13 | Для написания разных видов программ сейчас применяются разные языки программирования.
14 | Например, в сфере __мобильных__ программ сейчас правят бал языки Swift (мобильные устройства под управлением iOS), Java и Kotlin (устройства под управлением Android).
15 | __Системные__ программы, как правило, пишутся на языках C или {cpp}.
16 | Эти же языки долгое время использовались и для создания __встраиваемых__ программ, но в последние годы в этой области набирает популярность язык Java.
17 | Для написания web-клиентов часто используется JavaScript, а в простых случаях -- язык разметки страниц HTML.
18 | Web-серверы используют опять-таки Java (в сложных случаях), а также Python и PHP (в более простых).
19 | Наконец, простые desktop-программы сейчас могут быть написаны на самых разных языках, и выбор во многом зависит от сложности программы, области её использования, предполагаемой операционной системы.
20 | В первую очередь следует назвать языки Java, {cpp}, C#, Python, Visual Basic, Ruby, Swift.
21 |
22 | В каком-то смысле самым универсальным и одновременно самым распространённым языком программирования на данный момент следует считать язык Java.
23 | Java в широком смысле -- не только язык, но и платформа для выполнения программ под самыми разными операционными системами и на разной аппаратуре.
24 | Такая универсальность обеспечивается наличием виртуальной машины Java -- специальной системной программы, интерпретирующей Java байт-код в машинные коды конкретного компьютера или системы.
25 | Java также включает богатейший набор библиотек для разработки.
26 |
27 | Однако для начинающих язык Java является несколько многословным и сложным.
28 | Это пособие посвящено другому языку программирования, спутнику Java -- языку Котлин.
29 | Котлин -- молодой, лёгкий для изучения язык программирования, позволяющий писать программы под платформы JVM и Android более лаконично, просто и с меньшим количеством ошибок по сравнению с языком Java.
30 | Котлин и Java -- полностью __интероперабельные__ языки, поэтому одна и та же программа может быть частично написана на Котлине, частично на Java.
31 | Программы на Котлине могут использовать все имеющиеся Java-библиотеки, и наоборот.
32 | На данный момент программы на Котлине пишут сотни тысяч программистов, основная ниша его промышленного применения -- мобильные программы под платформу Android и, в несколько меньшей степени, web-разработка.
33 |
34 | В ходе изучения Котлина мы изучим также многие элементы стандартной библиотеки Java, а понимание работы программ на Котлине во многом упростит понимание работы Java-программ.
35 |
36 | == Что требуется для начала
37 |
38 | Самый простой способ начать программировать на Котлине -- зайти на сайт http://try.kotlinlang.org.
39 | Имеющаяся там "песочница" позволяет писать программы прямо в браузере, с возможностью выполнять и сохранять свои программы и проходить обучающие курсы.
40 |
41 | Масштабы песочницы, однако, достаточны только для небольших программ, а более-менее серьёзные программы, как правило, разрабатываются в интегрированной среде (IDE).
42 | Разработка под платформу Java в любом случае требует установки пакета JDK, который необходимо скачать с http://www.oracle.com/technetwork/java/javase/downloads/index.html[сайта компании Oracle].
43 | Первое время вам потребуется Java Platform, Standard Edition, рекомендуется 8-я её редакция, на сентябрь 2018 года последняя её версия -- Java SE 8u181.
44 |
45 | В качестве интегрированной среды разработки рекомендую установить IntelliJ IDEA Community Edition, её следует брать https://www.jetbrains.com/idea/download[отсюда].
46 | Community Edition является полностью бесплатной, базовая версия обеспечивает поддержку программирования на Java, Kotlin, Scala, Groovy, поддержку систем контроля версий Git, Mercurial, SVN, интеграцию с системами сборки Maven и Gradle.
47 |
48 | Для интеграции IDEA с системой контроля версий Git необходимо установить один из клиентов Git.
49 | Таких клиентов существует много; "родной" Git клиент можно скачать https://git-scm.com/downloads[здесь].
50 | Имейте в виду, что в IDEA интегрирован собственный Git-плагин, уже имеющий графический интерфейс, поэтому скачивать и устанавливать клиенты Git с графическим интерфейсом (GUI Clients) необязательно.
51 |
52 | == Учебный проект
53 |
54 | В ходе обучения мы будем активно использовать проект "Котлин как первый язык программирования", содержащий текст данного пособия и около сотни различных задач на языке Kotlin.
55 | Оригинальный код данного проекта доступен по адресу https://github.com/Kotlin-Polytech/KotlinAsFirst-Coursera на сайте GitHub, который является специализированным хранилищем программных кодов и основан на системе контроля версий Git.
56 | Для того, чтобы начать работать с этим проектом, Вам необходимо выполнить следующие действия.
57 |
58 | 1. Зарегистрироваться на https://github.com/ (в случае, если у Вас еще нет GitHub аккаунта). Далее выбранное Вами имя будет обозначаться как .
59 | 1. Создать специальную копию репозитория проекта -- _форк_. Для этого достаточно зайти на страницу проекта https://github.com/Kotlin-Polytech/KotlinAsFirst-Coursera и нажать кнопку `Fork` в правом верхнем углу страницы. После этого Ваша персональная копия проекта станет доступна по адресу https://github.com//KotlinAsFirst-Coursera, и всю работу по решению различных задач Вы должны выполнять именно с Вашей копией.
60 | 1. Для загрузки проекта в IntelliJ IDEA следует выполнить команду `Check out from Version Control` -> `GitHub` из окна `Welcome to Intellij IDEA` (или `File` -> `New` -> `Project from Version Control` -> `GitHub` из окна проекта), в появившемся окне ввести Git Repository URL https://github.com//KotlinAsFirst-Coursera и место на компьютере, куда будет скачан проект (Parent Directory).
61 | 1. Далее следуйте инструкциям среды для настройки проекта. Подробное руководство вы можете найти http://kspt.icc.spbstu.ru/media/files/2018/kaf/IdeaConfig.pdf[здесь].
62 |
63 | Проект содержит задачи, разбитые на девять уроков (lesson).
64 | Тексты задач доступны через окно `Project` в IntelliJ IDEA (открывается комбинацией клавиш `Alt + 1`).
65 | В папках `src/lessonX`, где X -- номер урока, находятся примеры решённых задач к данному уроку, тексты задач, которые необходимо решить, и готовые заглушки функций для написания решения.
66 | В папках test/lessonX находятся тестовые функции к задачам. Подробнее о задачах и тестах см. раздел 1 этого пособия.
67 |
68 | Работа с проектом будет осуществляться в рамках системы контроля версий Git, а Ваши решения будут представлять собой коммиты в соответствующий репозиторий.
69 | Коммит -- это законченное изменение текста проекта (например, решение одной или нескольких задач из какого-либо урока).
70 | Для более глубокого понимания того, что такое системы контроля версий, зачем они нужны и как с ними работать, рекомендуется пройти один или несколько обучающих уроков из Интернета.
71 | Несколько примеров подобных туториалов приведены ниже.
72 |
73 | * https://githowto.com/ru/
74 | * https://try.github.io/levels/1/challenges/1
75 |
76 | В ходе освоения различных уроков вы можете обращаться за помощью к преподавателю или своим однокурсникам, но важно, чтобы _хотя бы одна_ задача из каждого урока была решена вами самостоятельно.
77 | Имейте в виду, что задачи разбиты на пять уровней сложности, сложность задачи указана в комментарии к ней (тривиальная Trivial, простая Easy, средняя Normal, сложная Hard и очень сложная Impossible).
78 | Решайте в первую очередь те задачи, которые кажутся вам по силам, но иногда пытайтесь решать и более сложные.
79 | Если у вас нет идей, как решить какую-либо сложную задачу, -- попросите преподавателя о подсказке.
80 | Оценка по итогам курса будет зависеть от сложности решённых вами задач из различных уроков.
81 |
82 | Законченное решение задачи (или нескольких задач из одного урока) следует фиксировать в виде коммита в системе контроля версий. Для этого необходимо:
83 |
84 | 1. Зайти в окно `Version Control` среды Intellij IDEA. Это делается из меню `View` -> `Tool Windows`, либо с помощью комбинации клавиш `Alt + 9`.
85 | 1. В нём щелчком мыши выбрать вкладку `Local Changes` (локальные изменения). Убедитесь, что файлы, которые вы меняли, присутствуют в этом окне.
86 | 1. В контекстном меню выберите команду `Commit Changes`. В появившемся окне введите осмысленный комментарий к вашему изменению (например, "Решена задача такая-то"), откройте выпадающее меню справа от кнопки `Commit` и в нём выберете команду `Commit and Push`.
87 | 1. При появлении соответствующих окон введите своё имя и e-mail для идентификации автора коммита (эти поля заполняются один раз), а также логин и пароль для Вашего аккаунта на GitHub.
88 |
89 | == Вопросы
90 |
91 | В ходе изучения нового языка у вас, конечно, будут возникать вопросы, не стесняйтесь их задавать.
92 | Помимо обращения к вашим однокурсникам и преподавателям, у вас есть следующие возможности:
93 |
94 | * посмотреть "часто задаваемые вопросы" далее по тексту
95 | * поискать ответ на вопрос с помощью поисковой системы в Интернете
96 | * почитать разнообразную информацию о Котлине в его http://kotlinlang.org/docs/reference[документации]
97 | * задать нам вопрос в https://kotlinlang.slack.com[Kotlin Slack] (получить приглашение можно http://slack.kotlinlang.org/[здесь]) в канале https://kotlinlang.slack.com/messages/CCH9B1UPJ/convo/C0BQ5GZ0S-1511956674.000289/[russian-kotlinasfirst]
98 | * воспользоваться https://kotlinlang.org/community/[другими ресурсами для общения]
99 |
100 | Kotlin Slack -- это система общения, созданная специально для программистов на Котлине.
101 | Система состоит из множества каналов, посвящённых разным аспектам программирования на Котлине -- в большинстве из них общение идёт на английском языке.
102 | Нашему курсу посвящён канал russian-kotlinasfirst, и там вы сможете задать любые вопросы по этому курсу на русском языке.
103 | В качестве других важных каналов назову general -- канал с общими обсуждениями, касающимися Котлина, и russian -- общий канал для русскоязычных Котлин-программистов.
104 |
105 | === Часто задаваемые вопросы (F.A.Q.)
106 |
107 | * Что делать, если при открытии файла расширением .kt из учебного проекта (например, Simple.kt) вы видите сообщение над ним `Project SDK is not defined`?
108 |
109 | Нажмите на ссылку `Setup SDK` в правой части сообщения. Выберете JDK 1.8 для работы с проектом в появившемся окне.
110 | Если список JDK в окне пуст или не содержит JDK 1.8, следует нажать на клавишу `Configure`, затем зелёный плюс в верхнем левом углу и зарегистрировать установленную на Вашем компьютере JDK 1.8 в Intellij IDEA.
111 | Если Вы забыли установить JDK, это следует сделать, предварительно скачав её с сайта Oracle.
112 |
113 | * Что делать, если отсутствует зелёный треугольник напротив функции `main` и тестовых функций?
114 |
115 | Откройте окно Maven Projects на панели в правой части окна IDEA (если вы не видите там такой надписи, откройте его через меню -- View > Tool Windows > Maven Projects)
116 | и нажмите в нём на кнопку с изображением двух стрелок в круге. Дождитесь окончания импортирования Maven-проекта (наблюдайте за надписями в нижней части окна IDEA),
117 | после чего зелёные треугольники должны появиться. Проверьте также отсутствие надписи `Project SDK is not defined` в верхней части окна (см. вопрос выше).
118 |
119 | Если вам не удаётся открыть окно Maven Projects, попробуйте выйти из Intellij IDEA и войти в неё заново.
120 |
121 |
--------------------------------------------------------------------------------
/test/lesson9/task2/Tests.kt:
--------------------------------------------------------------------------------
1 | package lesson9.task2
2 |
3 | import lesson9.task1.Matrix
4 | import lesson9.task1.createMatrix
5 | import org.junit.jupiter.api.Assertions.*
6 | import org.junit.jupiter.api.Tag
7 | import org.junit.jupiter.api.Test
8 |
9 | class Tests {
10 | private fun createMatrix(height: Int, width: Int, values: List>): Matrix {
11 | val matrix = createMatrix(height, width, values[0][0])
12 | for (row in 0 until height) {
13 | for (column in 0 until width) {
14 | matrix[row, column] = values[row][column]
15 | }
16 | }
17 | return matrix
18 | }
19 |
20 | @Test
21 | @Tag("Example")
22 | fun transpose() {
23 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), transpose(createMatrix(1, 1, listOf(listOf(1)))))
24 | assertEquals(createMatrix(3, 4, listOf(listOf(1, 4, 6, 3), listOf(2, 5, 5, 2), listOf(3, 6, 4, 1))),
25 | transpose(createMatrix(4, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(6, 5, 4), listOf(3, 2, 1)))))
26 | }
27 |
28 | @Test
29 | @Tag("Example")
30 | fun plus() {
31 | assertEquals(createMatrix(1, 1, listOf(listOf(3))),
32 | createMatrix(1, 1, listOf(listOf(1))) + createMatrix(1, 1, listOf(listOf(2))))
33 | assertEquals(createMatrix(2, 2, listOf(listOf(2, 5), listOf(8, 11))),
34 | createMatrix(2, 2, listOf(listOf(1, 2), listOf(3, 4))) + createMatrix(2, 2, listOf(listOf(1, 3), listOf(5, 7))))
35 | }
36 |
37 | @Test
38 | @Tag("Hard")
39 | fun generateSpiral() {
40 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), generateSpiral(1, 1))
41 | assertEquals(createMatrix(2, 2,
42 | listOf(
43 | listOf(1, 2),
44 | listOf(4, 3)
45 | )), generateSpiral(2, 2))
46 | assertEquals(createMatrix(3, 4,
47 | listOf(
48 | listOf(1, 2, 3, 4),
49 | listOf(10, 11, 12, 5),
50 | listOf(9, 8, 7, 6)
51 | )), generateSpiral(3, 4))
52 | }
53 |
54 | @Test
55 | @Tag("Hard")
56 | fun generateRectangles() {
57 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), generateRectangles(1, 1))
58 | assertEquals(createMatrix(2, 2,
59 | listOf(
60 | listOf(1, 1),
61 | listOf(1, 1)
62 | )), generateRectangles(2, 2))
63 | assertEquals(createMatrix(4, 3,
64 | listOf(
65 | listOf(1, 1, 1),
66 | listOf(1, 2, 1),
67 | listOf(1, 2, 1),
68 | listOf(1, 1, 1)
69 | )), generateRectangles(4, 3))
70 | assertEquals(createMatrix(5, 6,
71 | listOf(
72 | listOf(1, 1, 1, 1, 1, 1),
73 | listOf(1, 2, 2, 2, 2, 1),
74 | listOf(1, 2, 3, 3, 2, 1),
75 | listOf(1, 2, 2, 2, 2, 1),
76 | listOf(1, 1, 1, 1, 1, 1)
77 | )), generateRectangles(5, 6))
78 |
79 | }
80 |
81 | @Test
82 | @Tag("Hard")
83 | fun generateSnake() {
84 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), generateSnake(1, 1))
85 | assertEquals(createMatrix(2, 2,
86 | listOf(
87 | listOf(1, 2),
88 | listOf(3, 4)
89 | )), generateSnake(2, 2))
90 | assertEquals(createMatrix(4, 2,
91 | listOf(
92 | listOf(1, 2),
93 | listOf(3, 4),
94 | listOf(5, 6),
95 | listOf(7, 8)
96 | )), generateSnake(4, 2))
97 | assertEquals(createMatrix(5, 4,
98 | listOf(
99 | listOf(1, 2, 4, 7),
100 | listOf(3, 5, 8, 11),
101 | listOf(6, 9, 12, 15),
102 | listOf(10, 13, 16, 18),
103 | listOf(14, 17, 19, 20)
104 | )), generateSnake(5, 4))
105 |
106 | }
107 |
108 | @Test
109 | @Tag("Normal")
110 | fun rotate() {
111 | val m = createMatrix(1, 1, listOf(listOf(("single"))))
112 | assertEquals(m, rotate(m))
113 | assertEquals(createMatrix(2, 2, listOf(listOf("alpha", "beta"),
114 | listOf("gamma", "delta"))),
115 | rotate(createMatrix(2, 2, listOf(listOf("beta", "delta"),
116 | listOf("alpha", "gamma")))))
117 | assertEquals(createMatrix(3, 3, listOf(listOf(7, 4, 1), listOf(8, 5, 2), listOf(9, 6, 3))),
118 | rotate(createMatrix(3, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9)))))
119 | }
120 |
121 | @Test
122 | @Tag("Hard")
123 | fun isLatinSquare() {
124 | assertTrue(isLatinSquare(createMatrix(1, 1, listOf(listOf(1)))))
125 | assertFalse(isLatinSquare(createMatrix(1, 1, listOf(listOf(2)))))
126 | assertTrue(isLatinSquare(createMatrix(2, 2, listOf(listOf(1, 2), listOf(2, 1)))))
127 | assertFalse(isLatinSquare(createMatrix(2, 2, listOf(listOf(1, 2), listOf(1, 2)))))
128 | assertTrue(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 1), listOf(1, 2, 3), listOf(3, 1, 2)))))
129 | assertFalse(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 1), listOf(1, 2, 3), listOf(3, 1, 4)))))
130 | assertFalse(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 1), listOf(1, 2, 3), listOf(1, 3, 2)))))
131 | assertFalse(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 0), listOf(1, 2, 3), listOf(3, 1, 2)))))
132 | }
133 |
134 | @Test
135 | @Tag("Normal")
136 | fun sumNeighbours() {
137 | assertEquals(createMatrix(1, 1, listOf(listOf(0))), sumNeighbours(createMatrix(1, 1, listOf(listOf(42)))))
138 | assertEquals(createMatrix(2, 2, listOf(listOf(9, 8), listOf(7, 6))),
139 | sumNeighbours(createMatrix(2, 2, listOf(listOf(1, 2), listOf(3, 4)))))
140 | assertEquals(createMatrix(4, 3, listOf(listOf(11, 19, 13), listOf(19, 31, 19),
141 | listOf(19, 31, 19), listOf(13, 19, 11))),
142 | sumNeighbours(createMatrix(4, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6),
143 | listOf(6, 5, 4), listOf(3, 2, 1)))))
144 | }
145 |
146 | @Test
147 | @Tag("Normal")
148 | fun findHoles() {
149 | assertEquals(Holes(listOf(), listOf()), findHoles(createMatrix(1, 1, listOf(listOf(1)))))
150 | assertEquals(Holes(listOf(0), listOf(0)), findHoles(createMatrix(1, 1, listOf(listOf(0)))))
151 | assertEquals(Holes(listOf(), listOf()), findHoles(createMatrix(2, 2, listOf(listOf(0, 1), listOf(1, 0)))))
152 | assertEquals(Holes(listOf(), listOf(0)), findHoles(createMatrix(2, 2, listOf(listOf(0, 1), listOf(0, 1)))))
153 | assertEquals(Holes(listOf(4), listOf(1, 3)),
154 | findHoles(createMatrix(5, 4, listOf(listOf(1, 0, 1, 0), listOf(0, 0, 1, 0), listOf(1, 0, 0, 0),
155 | listOf(0, 0, 1, 0), listOf(0, 0, 0, 0)))))
156 | }
157 |
158 | @Test
159 | @Tag("Normal")
160 | fun sumSubMatrix() {
161 | assertEquals(createMatrix(1, 1, listOf(listOf(0))), sumSubMatrix(createMatrix(1, 1, listOf(listOf(0)))))
162 | assertEquals(createMatrix(1, 5, listOf(listOf(1, 3, 6, 8, 9))),
163 | sumSubMatrix(createMatrix(1, 5, listOf(listOf(1, 2, 3, 2, 1)))))
164 | assertEquals(createMatrix(2, 2, listOf(listOf(2, 6), listOf(3, 10))),
165 | sumSubMatrix(createMatrix(2, 2, listOf(listOf(2, 4), listOf(1, 3)))))
166 | assertEquals(createMatrix(3, 3, listOf(listOf(1, 3, 6), listOf(5, 12, 21), listOf(12, 27, 45))),
167 | sumSubMatrix(createMatrix(3, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9)))))
168 | }
169 |
170 | @Test
171 | @Tag("Easy")
172 | fun unaryMinus() {
173 | assertEquals(createMatrix(1, 1, listOf(listOf(3))), -createMatrix(1, 1, listOf(listOf(-3))))
174 | assertEquals(createMatrix(2, 2, listOf(listOf(-1, -2), listOf(-3, -4))),
175 | -createMatrix(2, 2, listOf(listOf(1, 2), listOf(3, 4))))
176 | assertEquals(createMatrix(1, 3, listOf(listOf(-4, -6, -8))), -createMatrix(1, 3, listOf(listOf(4, 6, 8))))
177 | }
178 |
179 | @Test
180 | @Tag("Normal")
181 | fun times() {
182 | assertEquals(createMatrix(1, 1, listOf(listOf(2))),
183 | createMatrix(1, 1, listOf(listOf(1))) * createMatrix(1, 1, listOf(listOf(2))))
184 | assertEquals(createMatrix(1, 1, listOf(listOf(19))),
185 | createMatrix(1, 3, listOf(listOf(2, 3, 5))) * createMatrix(3, 1, listOf(listOf(4), listOf(2), listOf(1))))
186 | assertEquals(createMatrix(2, 2, listOf(listOf(23, -2), listOf(8, -18))),
187 | createMatrix(2, 3, listOf(listOf(3, 5, 1), listOf(2, -2, 4))) *
188 | createMatrix(3, 2, listOf(listOf(4, 1), listOf(2, 0), listOf(1, -5))))
189 | }
190 |
191 | @Test
192 | @Tag("Hard")
193 | fun canOpenLock() {
194 | assertFalse(canOpenLock(
195 | key = createMatrix(1, 1, listOf(listOf(1))),
196 | lock = createMatrix(1, 1, listOf(listOf(1)))).first)
197 | assertEquals(Triple(true, 0, 0), canOpenLock(
198 | key = createMatrix(1, 1, listOf(listOf(1))),
199 | lock = createMatrix(1, 1, listOf(listOf(0)))))
200 | assertEquals(Triple(true, 0, 1), canOpenLock(
201 | key = createMatrix(2, 2, listOf(listOf(1, 0), listOf(0, 1))),
202 | lock = createMatrix(3, 3, listOf(listOf(1, 0, 1), listOf(0, 1, 0), listOf(1, 1, 1)))))
203 | assertFalse(canOpenLock(
204 | key = createMatrix(2, 2, listOf(listOf(1, 1), listOf(1, 0))),
205 | lock = createMatrix(3, 3, listOf(listOf(1, 0, 1), listOf(0, 1, 0), listOf(1, 1, 1)))).first)
206 | }
207 |
208 | @Test
209 | @Tag("Hard")
210 | fun fifteenGameMoves() {
211 | val start = createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8),
212 | listOf(9, 10, 11, 12), listOf(13, 14, 15, 0)))
213 | assertEquals(start, fifteenGameMoves(start, listOf()))
214 | assertEquals(start, fifteenGameMoves(createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8),
215 | listOf(9, 10, 11, 12), listOf(0, 13, 14, 15))),
216 | listOf(13, 14, 15)))
217 | assertEquals(start, fifteenGameMoves(createMatrix(4, 4, listOf(listOf(1, 2, 3, 0), listOf(5, 6, 7, 4),
218 | listOf(9, 10, 11, 8), listOf(13, 14, 15, 12))),
219 | listOf(4, 8, 12)))
220 | assertEquals(createMatrix(4, 4, listOf(listOf(5, 7, 9, 1), listOf(2, 12, 14, 15),
221 | listOf(0, 4, 13, 6), listOf(3, 10, 11, 8))),
222 | fifteenGameMoves(createMatrix(4, 4, listOf(listOf(5, 7, 9, 1), listOf(2, 12, 14, 15),
223 | listOf(3, 4, 6, 8), listOf(10, 11, 13, 0))),
224 | listOf(8, 6, 13, 11, 10, 3)))
225 | try {
226 | fifteenGameMoves(start, listOf(1))
227 | assert(false) { "Exception expected" }
228 | }
229 | catch (e: IllegalStateException) {}
230 | catch (e: Throwable) {
231 | assert(false) { "IllegalStateException expected" }
232 | }
233 | }
234 |
235 | private fun Matrix.copy(): Matrix {
236 | val result = createMatrix(height, width, this[0, 0])
237 | for (row in 0 until height) {
238 | for (column in 0 until width) {
239 | result[row, column] = this[row, column]
240 | }
241 | }
242 | return result
243 | }
244 |
245 | private fun Matrix.assertSolution() {
246 | val copy = copy()
247 | val moves = fifteenGameSolution(this)
248 | val start1 = createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8),
249 | listOf(9, 10, 11, 12), listOf(13, 14, 15, 0)))
250 | val start2 = createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8),
251 | listOf(9, 10, 11, 12), listOf(13, 15, 14, 0)))
252 | val result = fifteenGameMoves(copy, moves)
253 | assertTrue(result == start1 || result == start2) { "Result position is not a solution position: $result" }
254 | }
255 |
256 | @Test
257 | @Tag("Impossible")
258 | fun fifteenGameSolution() {
259 | createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8),
260 | listOf(9, 10, 11, 12), listOf(13, 14, 15, 0))).assertSolution()
261 | createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8),
262 | listOf(9, 10, 11, 12), listOf(13, 15, 14, 0))).assertSolution()
263 | createMatrix(4, 4, listOf(listOf(5, 7, 9, 2), listOf(1, 12, 14, 15),
264 | listOf(3, 4, 6, 8), listOf(10, 11, 13, 0))).assertSolution()
265 | createMatrix(4, 4, listOf(listOf(0, 1, 2, 3), listOf(4, 5, 6, 7),
266 | listOf(8, 9, 10, 11), listOf(12, 13, 14, 15))).assertSolution()
267 | }
268 | }
--------------------------------------------------------------------------------
/tutorial/chapter10.adoc:
--------------------------------------------------------------------------------
1 | = 10. Дополнительные главы
2 |
3 | == Разбор и представление арифметических выражений
4 |
5 | Эта глава развивает содержимое шестого урока,
6 | отвечая на вопрос "А что делать, если простого разбиения строки на части недостаточно?".
7 | Одной из подобных ситуаций является разбор арифметических выражений.
8 | Для этого и других похожих случаев программисты придумали парсеры --
9 | специальные функции или объекты, выполняющие разбор сложных строк.
10 | Для написания парсеров используется теория формальных языков,
11 | которой мы (вкратце) коснёмся в этой главе.
12 |
13 | Рассмотрим пример.
14 | Пусть во входном файле содержится строчка, содержащая описание функции от `x`, например:
15 |
16 | [source]
17 | ----
18 | 3*x*x - (2 / x) + 7 -x
19 | ----
20 |
21 | Необходимо по имеющемуся списку значений `x` (например, 1, 2 и -1)
22 | рассчитать значения данной функции и вернуть их в виде ассоциативного массива
23 | (например, 1 -> 7, 2 -> 16, -1 -> 13).
24 |
25 | Предполагается, что функция является целочисленной,
26 | и в ней могут присутствовать четыре арифметических действия и круглые скобки.
27 |
28 | Как решать подобную задачу? Поэтапно.
29 |
30 | === 1. Лексический анализ
31 |
32 | Лексический анализ -- первый этап решения.
33 | Его цель -- разбиение исходного выражения на атомарные элементы,
34 | которыми в приведённом примере являются:
35 |
36 | [source]
37 | ----
38 | 3, *, x, *, x, -, (, 2, /, x, ), +, 7, -, x
39 | ----
40 |
41 | Обратите внимание, что в процессе лексического анализа из выражения выбрасываются пробелы.
42 |
43 | Один из способов лексического анализа -- применение регулярных выражений.
44 |
45 | [source,kotlin]
46 | ----
47 | fun String.parseExpr(): Expression {
48 | val matchResults = Regex("""x|\+|-|\*|/|\(|\)|\d+?| +?|.+?""").findAll(this)
49 | val groups = matchResults.map { it.value }.filter { it.isNotBlank() }.toList()
50 | return Parser(groups).parse()
51 | }
52 | ----
53 |
54 | Заданное в этой функции регулярное выражение задаёт описание атомарного элемента.
55 | Это может быть `x`, знаки четырёх арифметических действий, знаки скобок,
56 | пробелы, и все прочие символы.
57 | Лямбда `{ it.isNotBlank() }` убирает из имеющейся последовательности пробелы,
58 | оставляя всё остальное.
59 | После этого происходит переход к следующему этапу -- собственно разбору.
60 | Но перед этим поговорим о том, а что, собственно, мы хотим получить в результате разбора.
61 |
62 | === 2. Представление выражений
63 |
64 | Зададимся вопросом: как представить арифметическое выражение
65 | с помощью структур данных, имеющихся в Котлине?
66 | Перед ответом на этот вопрос стоит ответить на другой:
67 | а что такое (формально) арифметическое выражение в нашей задаче?
68 | В теории формальных языков ответ может выглядеть так:
69 |
70 | * это `x`,
71 | * или же, это целая константа,
72 | * или же, это операция (сложение / вычитание / умножение / деление) над двумя другими выражениями
73 | * или, наконец, это операция "минус выражение" над другим выражением
74 |
75 | Обратите внимание, что определение рекурсивно -- понятие выражения использует в процессе определения себя же.
76 | Это довольно частый случай для подобных формальных описаний.
77 | Подобная рекурсивная структура с вариантами в Котлине может быть описана
78 | так называемым __алгебраическим__ или __запечатанным__ (sealed) классом.
79 |
80 | [source,kotlin]
81 | ----
82 | sealed class Expression {
83 | object Variable : Expression()
84 |
85 | class Constant(val value: Int) : Expression()
86 |
87 | enum class Operation {
88 | PLUS,
89 | MINUS,
90 | TIMES,
91 | DIV;
92 | }
93 |
94 | class Binary(
95 | val left: Expression,
96 | val op: Operation,
97 | val right: Expression
98 | ) : Expression()
99 |
100 | class Negate(val arg: Expression) : Expression()
101 | }
102 | ----
103 |
104 | Алгебраический класс используется не сам по себе, а в виде его разновидностей,
105 | причём по правилам Котлина, для алгебраического класса эти разновидности должны быть описаны
106 | __внутри__ определения самого класса (для версии 1.1 и выше -- внутри того же файла).
107 |
108 | Для описания разновидностей `Expression` используется уже знакомый синтаксис `class Something(...) : Expression()`.
109 | Разновидности алгебраического класса содержат дополнительную информацию по сравнению с ним самим:
110 |
111 | * целая константа `Constant` содержит своё значение `value`
112 | * инвертированное выражение `Negate` содержит свой аргумент (например, для `-x` аргумент будет равен `x`)
113 | * выражение с двумя аргументами (бинарное) `Binary` содержит два своих аргумента и выполняемую операцию
114 |
115 | Обратите внимание на `enum class Operation`.
116 | Это не разновидность выражения (нет `: Expression()`),
117 | а просто дополнительный класс для описания операций с двумя аргументами.
118 | Слово `enum` означает __перечисление__ в том смысле, что существует ограниченное (в данном случае -- 4)
119 | количество операций -- сложение, умножение, вычитание, деление.
120 | Все операции перечисляются через запятую, в конце (необязательно) ставится точка с запятой.
121 |
122 | Последняя разновидность выражения `object Variable : Expression()`.
123 | В отличие от прочих разновидностей, эта является не классом, а __объектом__.
124 | В Котлине объект отличается от класса тем,
125 | что для данного типа существует __ровно один__ объект данного типа -- в данном случае это переменная `x`.
126 | У объекта нельзя вызывать конструкторы, а обращаются к нему просто по имени: в данном случае `Variable`.
127 | Если бы нас интересовали выражения с несколькими переменными,
128 | `Variable` тоже превратился бы в класс со свойством `name`: имя конкретной переменной.
129 |
130 | Подобное описание выражения позволяет с лёгкостью рассчитать его значение при заданном `x`:
131 |
132 | [source,kotlin]
133 | ----
134 | fun Expression.calculate(x: Int): Int = when (this) {
135 | Variable -> x
136 | is Constant -> value
137 | is Binary -> when (op) {
138 | PLUS -> left.calculate(x) + right.calculate(x)
139 | MINUS -> left.calculate(x) - right.calculate(x)
140 | TIMES -> left.calculate(x) * right.calculate(x)
141 | DIV -> left.calculate(x) / right.calculate(x)
142 | }
143 | is Negate -> -arg.calculate(x)
144 | }
145 | ----
146 |
147 | В данном `when` мы перебираем все возможные варианты выражения-получателя:
148 |
149 | * значение переменной равно заданному `x: Int`
150 | * значение константы равно её свойству `value`
151 | * значение `Negate` равно минус значению её выражения-аргумента
152 | * значение `Binary` рассчитывается аналогично с учётом конкретной операции
153 |
154 | === 3. Синтаксический анализ
155 |
156 | Этот этап иногда называется просто "разбор".
157 | Для его проведения, нам потребуется более подробное формальное описание выражения с учётом приоритетов операций.
158 | Скажем, операции в скобках всегда выполняются в первую очередь,
159 | умножение и деление -- во вторую, сложение и вычитание -- в третью.
160 | На основе этого описания можно составить __формальную грамматику__ выражения, выглядящую примерно так:
161 |
162 | * ВЫРАЖЕНИЕ ::= СЛАГАЕМОЕ +|- СЛАГАЕМОЕ +|- ... +|- СЛАГАЕМОЕ
163 | * СЛАГАЕМОЕ ::= МНОЖИТЕЛЬ *|/ МНОЖИТЕЛЬ *|/ ... *|/ МНОЖИТЕЛЬ
164 | * МНОЖИТЕЛЬ ::= (ВЫРАЖЕНИЕ) | -ВЫРАЖЕНИЕ | КОНСТАНТА | ПЕРЕМЕННАЯ
165 |
166 | То есть, выражение может включать в себя любое (в том числе одно) число слагаемых,
167 | разделённых операторами "плюс" и "минус".
168 | Слагаемое, в свою очередь, может включать в себя любое (в том числе одно) число множителей,
169 | разделённых операторами "умножить" и "разделить".
170 | Наконец, множитель -- это выражение в скобках, или с минусом впереди, или константа, или переменная.
171 | К примеру, в выражении `2 + 3 * x / (1 - x)` имеется два слагаемых -- `2` и `3 * x / (1 - x)`.
172 | Слагаемое `2` состоит из одного множителя, который, в свою очередь, является слагаемым.
173 | Второе слагаемое состоит из трёх множителей -- `3` (константа), `x` (переменная), `(1 - x)`.
174 | Последний из множителей, в свою очередь, является выражением `1 - x` в скобках, состоящим из двух слагаемых.
175 |
176 | Знак `::=` в этом определении приближённо означает "состоит из", то есть, мы описываем одно понятие через другие.
177 | А сама по себе формальная грамматика позволяет определить, является ли заданная строка выражением,
178 | а также разобраться, из каких частей это выражение состоит. Например:
179 |
180 | [source,kotlin]
181 | ----
182 | class Parser(private val groups: List) {
183 | private var pos = 0
184 |
185 | fun parse(): Expression {
186 | val result = parseExpression()
187 | if (pos < groups.size) {
188 | throw IllegalStateException("Unexpected expression remainder: ${groups.subList(pos, groups.size)}")
189 | }
190 | return result
191 | }
192 |
193 | private fun parseExpression(): Expression {
194 | var left = parseItem()
195 | while (pos < groups.size) {
196 | val op = operationMap[groups[pos]]
197 | when (op) {
198 | PLUS, MINUS -> {
199 | pos++
200 | val right = parseItem()
201 | left = Expression.Binary(left, op, right)
202 | }
203 | else -> return left
204 | }
205 | }
206 | return left
207 | }
208 |
209 | private val operationMap = mapOf("+" to PLUS, "-" to MINUS, "*" to TIMES, "/" to DIV)
210 |
211 | // ...
212 | }
213 | ----
214 |
215 | Данный класс `Parser` (разборщик, или просто парсер) имеет свойство `groups` -- список атомарных частей строки,
216 | полученный при лексическом анализе, а также изменяемое свойство `pos` --
217 | оно указывает, до какой из частей мы дошли в процессе разбора.
218 | Его функция `parse` осуществляет разбор всего выражения и проверяет,
219 | что была разобрана вся найденная строка и не осталось ничего в хвосте.
220 |
221 | Функция `parseExpression` соответствует определению выражения из грамматики.
222 | В соответствии с определением она разбирает первое слагаемое `parseItem`,
223 | затем, если найден плюс или минус -- второе, и составляет из них `Expression.Binary`.
224 | При наличии следующего плюса или минуса процедура повторяется.
225 | Для преобразования обозначения операции в объект перечисления `Operation` используется отображение `operationMap`.
226 |
227 | Результат функции `parseExpression`, как и других функций разбора -- `Expression`,
228 | то есть любая из описанных нами выше разновидностей алгебраического типа `Expression`.
229 | Обратите внимание, что для выражения с тремя и более слагаемыми, например, `2 + x - 1`,
230 | мы получим в итоге следующую структуру: `Binary(Binary(2, +, x), -, 1)`.
231 | То есть первым аргументом внешнего бинарного выражения `2 + x - 1`,
232 | в свою очередь, является бинарное выражение `2 + x`.
233 |
234 | Аналогичным образом устроена функция `parseItem`.
235 | Она соответствует определению слагаемого, для разбора множителей использует `parseFactor`,
236 | и в качестве разделителей ищет знаки умножения и деления:
237 |
238 | [source,kotlin]
239 | ----
240 | class Parser(val groups: List) {
241 | var pos = 0
242 |
243 | // ...
244 |
245 | private fun parseItem(): Expression {
246 | var left = parseFactor()
247 | while (pos < groups.size) {
248 | val op = operationMap[groups[pos]]
249 | when (op) {
250 | TIMES, DIV -> {
251 | pos++
252 | val right = parseFactor()
253 | left = Expression.Binary(left, op, right)
254 | }
255 | else -> return left
256 | }
257 | }
258 | return left
259 | }
260 | }
261 | ----
262 |
263 | Наконец, разбор множителя `parseFactor` анализирует один из четырёх вариантов:
264 | выражение в скобках, выражение с минусом, константа, переменная.
265 |
266 | [source,kotlin]
267 | ----
268 | class Parser(val groups: List) {
269 | var pos = 0
270 |
271 | // ...
272 | private fun parseFactor(): Expression =
273 | if (pos >= groups.size) throw IllegalStateException("Unexpected expression end")
274 | else {
275 | val group = groups[pos++]
276 | when (group) {
277 | "x" -> Expression.Variable
278 | "-" -> Expression.Negate(parseFactor())
279 | "(" -> {
280 | val arg = parseExpression()
281 | val next = groups[pos++]
282 | if (next == ")") arg
283 | else throw IllegalStateException(") expected instead of $next")
284 | }
285 | else -> Expression.Constant(group.toInt())
286 | }
287 | }
288 | }
289 | ----
290 |
291 | Обратите внимание, что в процессе анализа выражения в скобках мы повторно вызываем `parseExpression`,
292 | то есть, налицо рекурсия. Глубина рекурсии зависит от количества вложенных скобок в выражении.
293 |
294 | В результате действия подобного парсера упомянутое выше выражение `2 + 3 * x / (1 - x)`
295 | превратится в структуру `Binary(2, +, Binary(Binary(3, *, x), /, Binary(1, - x)))`.
296 |
297 | === 4. Объединяем всё вместе
298 |
299 | Наконец, рассмотрим функцию, решающую исходную задачу.
300 | Пусть во входном файле содержится строчка, содержащая описание функции от `x`.
301 | Необходимо по имеющемуся списку значений `x` (например, 1, 2 и -1)
302 | рассчитать значения данной функции и вернуть их в виде ассоциативного массива.
303 |
304 | [source,kotlin]
305 | ----
306 | fun parseExpr(inputName: String, values: List): Map {
307 | val expr = File(inputName).readLines().firstOrNull()?.parseExpr() ?: throw IllegalArgumentException()
308 | val result = mutableMapOf()
309 | for (value in values) {
310 | result[value] = expr.calculate(value)
311 | }
312 | return result
313 | }
314 | ----
315 |
316 | Данная функция читает первую строку файла `inputName` и разбирает её с помощью `String.parseExpr()`.
317 | Затем для каждого из значений из списка `values` вызывается функция `Expression.calculate`
318 | и результат кладётся в ассоциативный массив `result`. Таким образом, задача решена.
319 |
--------------------------------------------------------------------------------