├── input ├── expr_in3.txt ├── expr_in4.txt ├── expr_in1.txt ├── expr_in2.txt ├── trans_in1.txt ├── chaotic_in1.txt ├── markdown_simple.md ├── markdown_lists.md ├── center_in1.txt ├── sibilants_in1.txt ├── substrings_in1.txt ├── width_in1.txt └── align_in1.txt ├── slides ├── Kotlin00.pdf ├── Kotlin01.pdf ├── Kotlin02.pdf ├── Kotlin03.pdf ├── Kotlin04.pdf ├── Kotlin05.pdf ├── Kotlin06.pdf ├── Kotlin07.pdf ├── Kotlin08.pdf ├── Kotlin50.pdf ├── Kotlin04_2.pdf ├── Kotlin04_5.pdf ├── Kotlin05_5.pdf ├── Kotlin06_2.pdf ├── Kotlin06_5.pdf └── Kotlin08_5.pdf ├── .gitignore ├── README.md ├── test ├── lesson8 │ ├── task2 │ │ └── Tests.kt │ └── task1 │ │ └── Tests.kt ├── lesson7 │ ├── task1 │ │ └── Tests.kt │ └── task2 │ │ └── Tests.kt ├── lesson2 │ ├── task2 │ │ └── Tests.kt │ └── task1 │ │ └── Tests.kt ├── lesson6 │ ├── task3 │ │ └── Tests.kt │ ├── task1 │ │ └── Tests.kt │ └── task2 │ │ └── Tests.kt ├── lesson1 │ └── task1 │ │ └── Tests.kt ├── lesson5 │ └── task1 │ │ └── Tests.kt ├── lesson3 │ └── task1 │ │ └── Tests.kt └── lesson4 │ └── task1 │ └── Tests.kt ├── src ├── lesson2 │ ├── task2 │ │ └── Logical.kt │ └── task1 │ │ └── IfElse.kt ├── lesson7 │ ├── task1 │ │ └── Matrix.kt │ └── task2 │ │ └── Matrices.kt ├── lesson6 │ ├── task3 │ │ └── Graph.kt │ ├── task1 │ │ └── Geometry.kt │ └── task2 │ │ └── Chess.kt ├── lesson1 │ └── task1 │ │ └── Simple.kt ├── lesson8 │ ├── task2 │ │ └── Regex.kt │ └── task1 │ │ └── Files.kt ├── lesson3 │ └── task1 │ │ └── Loop.kt ├── lesson4 │ └── task1 │ │ └── List.kt └── lesson5 │ └── task1 │ └── Parse.kt ├── pom.xml └── tutorial ├── chapter05_5.adoc ├── chapter08.adoc ├── chapter00.adoc ├── extra.adoc └── chapter04_5.adoc /input/expr_in3.txt: -------------------------------------------------------------------------------- 1 | alpha -------------------------------------------------------------------------------- /input/expr_in4.txt: -------------------------------------------------------------------------------- 1 | -x *(x *x -x) -------------------------------------------------------------------------------- /input/expr_in1.txt: -------------------------------------------------------------------------------- 1 | 3*x*x - 2 / x + 7 -x -------------------------------------------------------------------------------- /input/expr_in2.txt: -------------------------------------------------------------------------------- 1 | (4 * x - 3) / (x - 2) -------------------------------------------------------------------------------- /input/trans_in1.txt: -------------------------------------------------------------------------------- 1 | Здравствуй, 2 | мир! -------------------------------------------------------------------------------- /slides/Kotlin00.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin00.pdf -------------------------------------------------------------------------------- /slides/Kotlin01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin01.pdf -------------------------------------------------------------------------------- /slides/Kotlin02.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin02.pdf -------------------------------------------------------------------------------- /slides/Kotlin03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin03.pdf -------------------------------------------------------------------------------- /slides/Kotlin04.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin04.pdf -------------------------------------------------------------------------------- /slides/Kotlin05.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin05.pdf -------------------------------------------------------------------------------- /slides/Kotlin06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin06.pdf -------------------------------------------------------------------------------- /slides/Kotlin07.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin07.pdf -------------------------------------------------------------------------------- /slides/Kotlin08.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin08.pdf -------------------------------------------------------------------------------- /slides/Kotlin50.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin50.pdf -------------------------------------------------------------------------------- /input/chaotic_in1.txt: -------------------------------------------------------------------------------- 1 | Карминовый 2 | Боязливый 3 | Некрасивый 4 | Остроумный 5 | БелогЛазый 6 | ФиолетОвый 7 | -------------------------------------------------------------------------------- /slides/Kotlin04_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin04_2.pdf -------------------------------------------------------------------------------- /slides/Kotlin04_5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin04_5.pdf -------------------------------------------------------------------------------- /slides/Kotlin05_5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin05_5.pdf -------------------------------------------------------------------------------- /slides/Kotlin06_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin06_2.pdf -------------------------------------------------------------------------------- /slides/Kotlin06_5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin06_5.pdf -------------------------------------------------------------------------------- /slides/Kotlin08_5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2016/HEAD/slides/Kotlin08_5.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | 4 | .idea 5 | target 6 | out 7 | 8 | *.iml 9 | *.ipr 10 | *.iws 11 | *.zip -------------------------------------------------------------------------------- /input/markdown_simple.md: -------------------------------------------------------------------------------- 1 | Lorem ipsum *dolor sit amet*, consectetur **adipiscing** elit. 2 | Vestibulum lobortis. ~~Est vehicula rutrum *suscipit*~~, ipsum ~~lib~~ero *placerat **tortor***. 3 | 4 | Suspendisse ~~et elit in enim tempus iaculis~~. 5 | -------------------------------------------------------------------------------- /input/markdown_lists.md: -------------------------------------------------------------------------------- 1 | * Утка по-пекински 2 | * Утка 3 | * Соус 4 | * Салат Оливье 5 | 1. Мясо 6 | * Или колбаса 7 | 2. Майонез 8 | 3. Картофель 9 | 4. Что-то там ещё 10 | * Помидоры 11 | * Фрукты 12 | 1. Бананы 13 | 23. Яблоки 14 | 1. Красные 15 | 2. Зелёные 16 | -------------------------------------------------------------------------------- /input/center_in1.txt: -------------------------------------------------------------------------------- 1 | Съешь же ещё этих мягких французских булок, да выпей чаю. 2 | Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства. 3 | Тест 4 | 5 | Hello World 6 | Во входном файле с именем inputName содержится некоторый текст. 7 | Вывести его в выходной файл с именем outputName, выровняв по центру. -------------------------------------------------------------------------------- /input/sibilants_in1.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Простая 3 | * 4 | * В русском языке, как правило, после букв Ж, Ч, Ш, Щ пишется И, А, У, а не Ы, Я, Ю. 5 | * Во входном файле с именем inputName содержытся некоторый текст. 6 | * Проверить текст во входном файле на соблюдение данного правила и вывести в выходной 7 | * файл outputName текст с исправленными ошыбками. 8 | * 9 | * Регистр заменённых букв следует сохранять. 10 | * 11 | * Исключения (жЮри, броШЮра, параШют) в рамках данного задания обрабатывать не нужно 12 | * 13 | * жЫ шЫ ЖИ Ши ЖЯ шЯ Жа ша жу шу жю щю чя шю щю щя жя жи жы жю чю чя 14 | */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Котлин как первый язык программирования 2 | 3 | Решите приведённые в проекте задачи, чтобы научиться программировать на Котлине. Сейчас доступны восемь групп задач (уроков): 4 | 5 | 1. Простые функции. 6 | 2. Ветвления. 7 | 3. Циклы. 8 | 4. Работа со списками. 9 | 5. Разбор строк. 10 | 6. Простые классы. 11 | 7. Сложные классы на примере матриц. 12 | 8. Работа с файлами. 13 | 14 | В директории tutorial имеется учебное пособие по Котлину на русском языке. 15 | Оно содержит введение и восемь разделов, каждый из которых относится к соотвествующему уроку в проекте. 16 | Также пособие включает дополнительные разделы 4.5 (про организацию памяти), 17 | 5.5 (про регулярные выражения) и 6.5 (про графы и графовые алгоритмы). 18 | -------------------------------------------------------------------------------- /test/lesson8/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson8.task2 2 | 3 | import org.junit.jupiter.api.Tag 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.Assertions.* 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun parseExpr() { 11 | assertEquals(mapOf(1 to 7, 2 to 16, -1 to 13), parseExpr("input/expr_in1.txt", listOf(1, 2, -1))) 12 | assertEquals(mapOf(1 to -1, 3 to 9, 4 to 6), parseExpr("input/expr_in2.txt", listOf(1, 3, 4))) 13 | try { 14 | parseExpr("input/expr_in3.txt", listOf(0)) 15 | throw AssertionError("NumberFormatException expected") 16 | } 17 | catch (e: NumberFormatException) {} 18 | assertEquals(mapOf(-2 to 12, -1 to 2, 0 to 0, 1 to 0, 2 to -4), parseExpr("input/expr_in4.txt", listOf(-2, -1, 0, 1, 2))) 19 | } 20 | } -------------------------------------------------------------------------------- /input/substrings_in1.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Средняя 3 | * 4 | * Во входном файле с именем inputName имеется словарь с одним словом в каждой строчке. 5 | * Выбрать из данного словаря наиболее длинное слово, 6 | * в котором все буквы разные, например: Неряшливость, Четырёхдюймовка. 7 | * Вывести его в выходной файл с именем outputName. 8 | * Если во входном файле имеется несколько слов с одинаковой длиной, в которых все буквы разные, 9 | * в выходной файл следует вывести их все через запятую. 10 | * Регистр букв игнорировать, то есть буквы е и Е считать одинаковыми. 11 | * 12 | * Пример входного файла: 13 | * Карминовый 14 | * Боязливый 15 | * Некрасивый 16 | * Остроумный 17 | * БелогЛазый 18 | * ФиолетОвый 19 | 20 | * Соответствующий выходной файл: 21 | * Карминовый, Некрасивый 22 | * 23 | * Обратите внимание: данная функция не имеет возвращаемого значения 24 | */ -------------------------------------------------------------------------------- /input/width_in1.txt: -------------------------------------------------------------------------------- 1 | Простая 2 | 3 | Во входном файле с именем inputName содержится некоторый текст. 4 | Вывести его в выходной файл с именем outputName, выровняв по левому и правому краю относительно 5 | самой длинной строки. 6 | Выравнивание производить, вставляя дополнительные пробелы между словами: равномерно по всей строке 7 | 8 | Слова внутри строки отделяются друг от друга одним или более пробелом. 9 | 10 | Следующие правила должны быть выполнены: 11 | 1) Каждая строка входного и выходного файла не должна заканчиваться пробелом. 12 | 2) Пустые строки или строки из пробелов во входном файле должны превратиться в пустые строки в выходном файле. 13 | 3) Число строк в выходном файле должно быть равно числу строк во входном (в т. ч. пустых). 14 | 15 | Равномерность определяется следующими формальными правилами: 16 | 1) Число пробелов между каждыми двумя парами соседних слов не должно отличаться более, чем на 1. 17 | 2) Число пробелов между более левой парой соседних слов должно быть больше или равно числу пробелов 18 | между более правой парой соседних слов. -------------------------------------------------------------------------------- /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/lesson7/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson7.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..matrix.height - 1) { 22 | for (column in 0..matrix.width - 1) { 23 | matrix[row, column] = value++ 24 | } 25 | } 26 | value = 0 27 | for (row in 0..matrix.height - 1) { 28 | for (column in 0..matrix.width - 1) { 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..matrix.height - 1) { 41 | for (column in 0..matrix.width - 1) { 42 | matrix[Cell(row, column)] = strings[index++] 43 | } 44 | } 45 | index = 0 46 | for (row in 0..matrix.height - 1) { 47 | for (column in 0..matrix.width - 1) { 48 | assertEquals(strings[index++], matrix[row, column]) 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /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("Normal") 35 | fun brickPasses() { 36 | assertTrue(brickPasses(2, 10, 5, 6, 3)) 37 | assertTrue(brickPasses(4, 4, 4, 4, 4)) 38 | assertFalse(brickPasses(6, 5, 4, 3, 6)) 39 | assertTrue(brickPasses(3, 2, 1, 1, 2)) 40 | } 41 | 42 | @Test 43 | @Tag("Normal") 44 | fun circleInside() { 45 | assertFalse(circleInside(0.0, 0.0, 6.0, 0.0, 0.0, 5.0)) 46 | assertFalse(circleInside(0.0, 0.0, 1.0, 10.0, 10.0, 9.0)) 47 | assertTrue(circleInside(2.0, 2.0, 2.0, 2.0, 2.0, 2.0)) 48 | assertTrue(circleInside(-2.0, 3.0, 2.0, -2.0, 0.0, 5.0)) 49 | assertFalse(circleInside(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) 50 | } 51 | } -------------------------------------------------------------------------------- /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 | fun queenThreatens(x1: Int, y1: Int, x2: Int, y2: Int): Boolean = TODO() 29 | 30 | /** 31 | * Средняя 32 | * 33 | * Проверить, лежит ли окружность с центром в (x1, y1) и радиусом r1 целиком внутри 34 | * окружности с центром в (x2, y2) и радиусом r2. 35 | * Вернуть true, если утверждение верно 36 | */ 37 | fun circleInside(x1: Double, y1: Double, r1: Double, 38 | x2: Double, y2: Double, r2: Double): Boolean = TODO() 39 | 40 | /** 41 | * Средняя 42 | * 43 | * Определить, пройдет ли кирпич со сторонами а, b, c сквозь прямоугольное отверстие в стене со сторонами r и s. 44 | * Стороны отверстия должны быть параллельны граням кирпича. 45 | * Считать, что совпадения длин сторон достаточно для прохождения кирпича, т.е., например, 46 | * кирпич 4 х 4 х 4 пройдёт через отверстие 4 х 4. 47 | * Вернуть true, если кирпич пройдёт 48 | */ 49 | fun brickPasses(a: Int, b: Int, c: Int, r: Int, s: Int): Boolean = TODO() 50 | -------------------------------------------------------------------------------- /src/lesson7/task1/Matrix.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER", "unused") 2 | package lesson7.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 | -------------------------------------------------------------------------------- /test/lesson6/task3/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson6.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 | } -------------------------------------------------------------------------------- /src/lesson6/task3/Graph.kt: -------------------------------------------------------------------------------- 1 | package lesson6.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.filter { it !in visited } 66 | .map { dfs(it, finish, visited + start) } 67 | .filterNotNull().min() 68 | if (min == null) null else min + 1 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /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 minBiRoot() { 11 | assertEquals(Double.NaN, minBiRoot(0.0, 0.0, 1.0), 1e-2) 12 | assertEquals(Double.NaN, minBiRoot(0.0, 1.0, 2.0), 1e-2) 13 | assertEquals(-2.0, minBiRoot(0.0, 1.0, -4.0), 1e-10) 14 | assertEquals(Double.NaN, minBiRoot(1.0, -2.0, 4.0), 1e-2) 15 | assertEquals(Double.NaN, minBiRoot(1.0, 3.0, 2.0), 1e-2) 16 | assertEquals(-1.41, minBiRoot(1.0, -3.0, 2.0), 1e-2) 17 | } 18 | 19 | @Test 20 | @Tag("Easy") 21 | fun ageDescription() { 22 | assertEquals("21 год", ageDescription(21)) 23 | assertEquals("132 года", ageDescription(132)) 24 | assertEquals("12 лет", ageDescription(12)) 25 | assertEquals("199 лет", ageDescription(199)) 26 | } 27 | 28 | @Test 29 | @Tag("Easy") 30 | fun timeForHalfWay() { 31 | assertEquals(2.5, timeForHalfWay(1.0, 5.0, 2.0, 4.0, 3.0, 3.0), 1e-2) 32 | assertEquals(3.67, timeForHalfWay(4.0, 3.0, 1.0, 4.0, 1.0, 6.0), 1e-2) 33 | assertEquals(4.4, timeForHalfWay(3.0, 0.0, 1.0, 6.0, 2.0, 5.0), 1e-2) 34 | } 35 | 36 | @Test 37 | @Tag("Easy") 38 | fun whichRookThreatens() { 39 | assertEquals(0, whichRookThreatens(1, 2, 3, 4, 5, 6)) 40 | assertEquals(1, whichRookThreatens(5, 3, 7, 3, 4, 8)) 41 | assertEquals(2, whichRookThreatens(6, 8, 8, 6, 6, 3)) 42 | assertEquals(3, whichRookThreatens(3, 7, 8, 7, 3, 5)) 43 | } 44 | 45 | @Test 46 | @Tag("Easy") 47 | fun rookOrBishopThreatens() { 48 | assertEquals(0, rookOrBishopThreatens(4, 5, 5, 7, 8, 8)) 49 | assertEquals(1, rookOrBishopThreatens(2, 8, 6, 8, 1, 6)) 50 | assertEquals(2, rookOrBishopThreatens(5, 4, 3, 7, 1, 8)) 51 | assertEquals(3, rookOrBishopThreatens(1, 6, 7, 6, 3, 8)) 52 | } 53 | 54 | @Test 55 | @Tag("Easy") 56 | fun triangleKind() { 57 | assertEquals(-1, triangleKind(3.0, 7.5, 4.0)) 58 | assertEquals(1, triangleKind(5.0, 3.0, 4.0)) 59 | assertEquals(2, triangleKind(4.0, 6.0, 8.0)) 60 | assertEquals(0, triangleKind(1.0, 1.5, 1.5)) 61 | } 62 | 63 | @Test 64 | @Tag("Normal") 65 | fun segmentLength() { 66 | assertEquals(-1, segmentLength(1, 2, 3, 4)) 67 | assertEquals(-1, segmentLength(5, 7, 1, 3)) 68 | assertEquals(0, segmentLength(1, 2, 2, 4)) 69 | assertEquals(3, segmentLength(3, 6, 0, 9)) 70 | assertEquals(2, segmentLength(2, 5, 3, 9)) 71 | assertEquals(1, segmentLength(3, 6, 1, 4)) 72 | assertEquals(4, segmentLength(1, 15, 10, 14)) 73 | } 74 | } -------------------------------------------------------------------------------- /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 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun sqr() { 11 | assertEquals(0.0, sqr(0.0), 1e-5) 12 | assertEquals(4.0, sqr(2.0), 1e-5) 13 | assertEquals(9.0, sqr(-3.0), 1e-5) 14 | } 15 | 16 | @Test 17 | @Tag("Example") 18 | fun discriminant() { 19 | assertEquals(0.0, discriminant(0.0, 0.0, 0.0), 1e-5) 20 | assertEquals(0.0, discriminant(1.0, -2.0, 1.0), 1e-5) 21 | assertEquals(1.0, discriminant(1.0, 3.0, 2.0), 1e-5) 22 | } 23 | 24 | @Test 25 | @Tag("Example") 26 | fun sqRoot() { 27 | assertEquals(1.0, sqRoot(1.0, -2.0, 1.0), 1e-5) 28 | assertEquals(-3.0, sqRoot(1.0, 6.0, 9.0), 1e-5) 29 | } 30 | 31 | @Test 32 | @Tag("Example") 33 | fun quadraticRootProduct() { 34 | assertEquals(1.0, quadraticRootProduct(1.0, -2.0, 1.0), 1e-5) 35 | assertEquals(9.0, quadraticRootProduct(1.0, 6.0, 9.0), 1e-5) 36 | assertEquals(2.0, quadraticRootProduct(1.0, 3.0, 2.0), 1e-5) 37 | } 38 | 39 | @Test 40 | @Tag("Trivial") 41 | fun seconds() { 42 | assertEquals(30035, seconds(8, 20, 35)) 43 | assertEquals(86400, seconds(24, 0, 0)) 44 | assertEquals(13, seconds(0, 0, 13)) 45 | } 46 | 47 | @Test 48 | @Tag("Trivial") 49 | fun lengthInMeters() { 50 | assertEquals(18.98, lengthInMeters(8, 2, 11), 1e-2) 51 | assertEquals(2.13, lengthInMeters(1, 0, 0), 1e-2) 52 | } 53 | 54 | @Test 55 | @Tag("Trivial") 56 | fun angleInRadian() { 57 | assertEquals(0.63256, angleInRadian(36, 14, 35), 1e-5) 58 | assertEquals(Math.PI / 2.0, angleInRadian(90, 0, 0), 1e-5) 59 | } 60 | 61 | @Test 62 | @Tag("Trivial") 63 | fun trackLength() { 64 | assertEquals(5.0, trackLength(3.0, 0.0, 0.0, 4.0), 1e-5) 65 | assertEquals(1.0, trackLength(0.0, 1.0, -1.0, 1.0), 1e-5) 66 | assertEquals(1.41, trackLength(1.0, 1.0, 2.0, 2.0), 1e-2) 67 | } 68 | 69 | @Test 70 | @Tag("Easy") 71 | fun thirdDigit() { 72 | assertEquals(8, thirdDigit(3801)) 73 | assertEquals(1, thirdDigit(100)) 74 | assertEquals(0, thirdDigit(1000)) 75 | } 76 | 77 | @Test 78 | @Tag("Easy") 79 | fun travelMinutes() { 80 | assertEquals(216, travelMinutes(9, 25, 13, 1)) 81 | assertEquals(1, travelMinutes(21, 59, 22, 0)) 82 | } 83 | 84 | @Test 85 | @Tag("Easy") 86 | fun accountInThreeYears() { 87 | assertEquals(133.1, accountInThreeYears(100, 10), 1e-2) 88 | assertEquals(1.0, accountInThreeYears(1, 0), 1e-2) 89 | assertEquals(104.0, accountInThreeYears(13, 100), 1e-2) 90 | } 91 | 92 | @Test 93 | @Tag("Easy") 94 | fun numberRevert() { 95 | assertEquals(874, numberRevert(478)) 96 | assertEquals(201, numberRevert(102)) 97 | } 98 | } -------------------------------------------------------------------------------- /src/lesson2/task1/IfElse.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson2.task1 3 | 4 | import lesson1.task1.discriminant 5 | 6 | /** 7 | * Пример 8 | * 9 | * Найти наименьший корень биквадратного уравнения ax^4 + bx^2 + c = 0 10 | */ 11 | fun minBiRoot(a: Double, b: Double, c: Double): Double { 12 | // 1: в главной ветке if выполняется НЕСКОЛЬКО операторов 13 | if (a == 0.0) { 14 | if (b == 0.0) return Double.NaN // ... и ничего больше не делать 15 | val bc = -c / b 16 | if (bc < 0.0) return Double.NaN // ... и ничего больше не делать 17 | return -Math.sqrt(bc) 18 | // Дальше функция при a == 0.0 не идёт 19 | } 20 | val d = discriminant(a, b, c) // 2 21 | if (d < 0.0) return Double.NaN // 3 22 | // 4 23 | val y1 = (-b + Math.sqrt(d)) / (2 * a) 24 | val y2 = (-b - Math.sqrt(d)) / (2 * a) 25 | val y3 = Math.max(y1, y2) // 5 26 | if (y3 < 0.0) return Double.NaN // 6 27 | return -Math.sqrt(y3) // 7 28 | } 29 | 30 | /** 31 | * Простая 32 | * 33 | * Мой возраст. Для заданного 0 < n < 200, рассматриваемого как возраст человека, 34 | * вернуть строку вида: «21 год», «32 года», «12 лет». 35 | */ 36 | fun ageDescription(age: Int): String = TODO() 37 | 38 | /** 39 | * Простая 40 | * 41 | * Путник двигался t1 часов со скоростью v1 км/час, затем t2 часов — со скоростью v2 км/час 42 | * и t3 часов — со скоростью v3 км/час. 43 | * Определить, за какое время он одолел первую половину пути? 44 | */ 45 | fun timeForHalfWay(t1: Double, v1: Double, 46 | t2: Double, v2: Double, 47 | t3: Double, v3: Double): Double = TODO() 48 | 49 | /** 50 | * Простая 51 | * 52 | * Нa шахматной доске стоят черный король и две белые ладьи (ладья бьет по горизонтали и вертикали). 53 | * Определить, не находится ли король под боем, а если есть угроза, то от кого именно. 54 | * Вернуть 0, если угрозы нет, 1, если угроза только от первой ладьи, 2, если только от второй ладьи, 55 | * и 3, если угроза от обеих ладей. 56 | */ 57 | fun whichRookThreatens(kingX: Int, kingY: Int, 58 | rookX1: Int, rookY1: Int, 59 | rookX2: Int, rookY2: Int): Int = TODO() 60 | 61 | /** 62 | * Простая 63 | * 64 | * На шахматной доске стоят черный король и белые ладья и слон 65 | * (ладья бьет по горизонтали и вертикали, слон — по диагоналям). 66 | * Проверить, есть ли угроза королю и если есть, то от кого именно. 67 | * Вернуть 0, если угрозы нет, 1, если угроза только от ладьи, 2, если только от слона, 68 | * и 3, если угроза есть и от ладьи и от слона. 69 | */ 70 | fun rookOrBishopThreatens(kingX: Int, kingY: Int, 71 | rookX: Int, rookY: Int, 72 | bishopX: Int, bishopY: Int): Int = TODO() 73 | 74 | /** 75 | * Простая 76 | * 77 | * Треугольник задан длинами своих сторон a, b, c. 78 | * Проверить, является ли данный треугольник остроугольным (вернуть 0), 79 | * прямоугольным (вернуть 1) или тупоугольным (вернуть 2). 80 | * Если такой треугольник не существует, вернуть -1. 81 | */ 82 | fun triangleKind(a: Double, b: Double, c: Double): Int = TODO() 83 | 84 | /** 85 | * Средняя 86 | * 87 | * Даны четыре точки на одной прямой: A, B, C и D. 88 | * Координаты точек a, b, c, d соответственно, b >= a, d >= c. 89 | * Найти длину пересечения отрезков AB и CD. 90 | * Если пересечения нет, вернуть -1. 91 | */ 92 | fun segmentLength(a: Int, b: Int, c: Int, d: Int): Int = TODO() -------------------------------------------------------------------------------- /src/lesson1/task1/Simple.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson1.task1 3 | 4 | import java.lang.Math.* 5 | 6 | /** 7 | * Пример 8 | * 9 | * Вычисление квадрата вещественного числа 10 | */ 11 | fun sqr(x: Double) = x * x 12 | 13 | /** 14 | * Пример 15 | * 16 | * Вычисление дискриминанта квадратного уравнения 17 | */ 18 | fun discriminant(a: Double, b: Double, c: Double) = sqr(b) - 4 * a * c 19 | 20 | /** 21 | * Пример 22 | * 23 | * Поиск одного из корней квадратного уравнения 24 | */ 25 | fun sqRoot(a: Double, b: Double, c: Double) = (-b + sqrt(discriminant(a, b, c))) / (2 * a) 26 | 27 | /** 28 | * Пример 29 | * 30 | * Поиск произведения корней квадратного уравнения 31 | */ 32 | fun quadraticRootProduct(a: Double, b: Double, c: Double): Double { 33 | val sd = sqrt(discriminant(a, b, c)) 34 | val x1 = (-b + sd) / (2 * a) 35 | val x2 = (-b - sd) / (2 * a) 36 | return x1 * x2 // Результат 37 | } 38 | 39 | /** 40 | * Пример главной функции 41 | */ 42 | fun main(args: Array) { 43 | // Решаем x^2 - 3*x + 2 = 0 44 | val x1x2 = quadraticRootProduct(1.0, -3.0, 2.0) 45 | println("Root product: $x1x2") 46 | } 47 | 48 | /** 49 | * Тривиальная 50 | * 51 | * Пользователь задает время в часах, минутах и секундах, например, 8:20:35. 52 | * Рассчитать время в секундах, прошедшее с начала суток (30035 в данном случае). 53 | */ 54 | fun seconds(hours: Int, minutes: Int, seconds: Int): Int = TODO() 55 | 56 | /** 57 | * Тривиальная 58 | * 59 | * Пользователь задает длину отрезка в саженях, аршинах и вершках (например, 8 саженей 2 аршина 11 вершков). 60 | * Определить длину того же отрезка в метрах (в данном случае 18.98). 61 | * 1 сажень = 3 аршина = 48 вершков, 1 вершок = 4.445 см. 62 | */ 63 | fun lengthInMeters(sagenes: Int, arshins: Int, vershoks: Int): Double = TODO() 64 | 65 | /** 66 | * Тривиальная 67 | * 68 | * Пользователь задает угол в градусах, минутах и секундах (например, 36 градусов 14 минут 35 секунд). 69 | * Вывести значение того же угла в радианах (например, 0.63256). 70 | */ 71 | fun angleInRadian(grad: Int, min: Int, sec: Int): Double = TODO() 72 | 73 | /** 74 | * Тривиальная 75 | * 76 | * Найти длину отрезка, соединяющего точки на плоскости с координатами (x1, y1) и (x2, y2). 77 | * Например, расстояние между (3, 0) и (0, 4) равно 5 78 | */ 79 | fun trackLength(x1: Double, y1: Double, x2: Double, y2: Double): Double = TODO() 80 | 81 | /** 82 | * Простая 83 | * 84 | * Пользователь задает целое число, большее 100 (например, 3801). 85 | * Определить третью цифру справа в этом числе (в данном случае 8). 86 | */ 87 | fun thirdDigit(number: Int): Int = TODO() 88 | 89 | /** 90 | * Простая 91 | * 92 | * Поезд вышел со станции отправления в h1 часов m1 минут (например в 9:25) и 93 | * прибыл на станцию назначения в h2 часов m2 минут того же дня (например в 13:01). 94 | * Определите время поезда в пути в минутах (в данном случае 216). 95 | */ 96 | fun travelMinutes(hoursDepart: Int, minutesDepart: Int, hoursArrive: Int, minutesArrive: Int): Int = TODO() 97 | 98 | /** 99 | * Простая 100 | * 101 | * Человек положил в банк сумму в s рублей под p% годовых (проценты начисляются в конце года). 102 | * Сколько денег будет на счету через 3 года (с учётом сложных процентов)? 103 | * Например, 100 рублей под 10% годовых превратятся в 133.1 рубля 104 | */ 105 | fun accountInThreeYears(initial: Int, percent: Int): Double = TODO() 106 | 107 | /** 108 | * Простая 109 | * 110 | * Пользователь задает целое трехзначное число (например, 478). 111 | *Необходимо вывести число, полученное из заданного перестановкой цифр в обратном порядке (например, 874). 112 | */ 113 | fun numberRevert(number: Int): Int = TODO() 114 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.spbstu 8 | kfirst 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | 1.0.3 13 | 5.0.0-M2 14 | 4.12.0-M2 15 | 1.0.0-M2 16 | 17 | 18 | 19 | org.jetbrains.kotlin 20 | kotlin-stdlib 21 | ${kotlin.version} 22 | 23 | 24 | org.jetbrains.kotlin 25 | kotlin-reflect 26 | ${kotlin.version} 27 | 28 | 29 | org.jetbrains.kotlin 30 | kotlin-runtime 31 | ${kotlin.version} 32 | 33 | 34 | 35 | org.junit.jupiter 36 | junit-jupiter-api 37 | ${junit.jupiter.version} 38 | test 39 | 40 | 41 | org.junit.jupiter 42 | junit-jupiter-engine 43 | ${junit.jupiter.version} 44 | test 45 | 46 | 47 | org.junit.vintage 48 | junit-vintage-engine 49 | ${junit.vintage.version} 50 | test 51 | 52 | 53 | 54 | src 55 | test 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-compiler-plugin 60 | 3.5.1 61 | 62 | 1.8 63 | 1.8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | maven-surefire-plugin 70 | 2.19 71 | 72 | 73 | org.junit.platform 74 | 75 | junit-platform-surefire-provider 76 | 77 | ${junit.platform.version} 78 | 79 | 80 | 81 | 82 | kotlin-maven-plugin 83 | org.jetbrains.kotlin 84 | ${kotlin.version} 85 | 86 | 87 | compile 88 | process-sources 89 | 90 | compile 91 | 92 | 93 | 94 | test-compile 95 | process-test-sources 96 | 97 | test-compile 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /test/lesson5/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson5.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 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun timeStrToSeconds() { 11 | assertEquals(36000, timeStrToSeconds("10:00:00")) 12 | assertEquals(41685, timeStrToSeconds("11:34:45")) 13 | assertEquals(86399, timeStrToSeconds("23:59:59")) 14 | } 15 | 16 | @Test 17 | @Tag("Example") 18 | fun timeSecondsToStr() { 19 | assertEquals("10:00:00", timeSecondsToStr(36000)) 20 | assertEquals("11:34:45", timeSecondsToStr(41685)) 21 | assertEquals("23:59:59", timeSecondsToStr(86399)) 22 | } 23 | 24 | @Test 25 | @Tag("Normal") 26 | fun dateStrToDigit() { 27 | assertEquals("15.07.2016", dateStrToDigit("15 июля 2016")) 28 | assertEquals("", dateStrToDigit("3 мартобря 1918")) 29 | assertEquals("18.11.2018", dateStrToDigit("18 ноября 2018")) 30 | assertEquals("", dateStrToDigit("23")) 31 | assertEquals("03.04.2011", dateStrToDigit("3 апреля 2011")) 32 | } 33 | 34 | @Test 35 | @Tag("Normal") 36 | fun dateDigitToStr() { 37 | assertEquals("15 июля 2016", dateDigitToStr("15.07.2016")) 38 | assertEquals("", dateDigitToStr("01.02.20.19")) 39 | assertEquals("", dateDigitToStr("28.00.2000")) 40 | assertEquals("3 апреля 2011", dateDigitToStr("03.04.2011")) 41 | assertEquals("", dateDigitToStr("ab.cd.ef")) 42 | } 43 | 44 | @Test 45 | @Tag("Hard") 46 | fun flattenPhoneNumber() { 47 | assertEquals("+79211234567", flattenPhoneNumber("+7 (921) 123-45-67")) 48 | assertEquals("123456798", flattenPhoneNumber("12 -- 34- 5 -- 67 -98")) 49 | assertEquals("", flattenPhoneNumber("ab-123")) 50 | assertEquals("+12345", flattenPhoneNumber("+12 (3) 4-5")) 51 | assertEquals("", flattenPhoneNumber("134_+874")) 52 | } 53 | 54 | @Test 55 | @Tag("Normal") 56 | fun bestLongJump() { 57 | assertEquals(717, bestLongJump("706 % - 717 - 703")) 58 | assertEquals(-1, bestLongJump("% - - % -")) 59 | assertEquals(754, bestLongJump("700 717 707 % 754")) 60 | assertEquals(-1, bestLongJump("700 + 700")) 61 | 62 | } 63 | 64 | @Test 65 | @Tag("Hard") 66 | fun bestHighJump() { 67 | assertEquals(226, bestHighJump("226 +")) 68 | assertEquals(-1, bestHighJump("???")) 69 | assertEquals(230, bestHighJump("220 + 224 %+ 228 %- 230 + 232 %%- 234 %")) 70 | } 71 | 72 | @Test 73 | @Tag("Hard") 74 | fun plusMinus() { 75 | assertEquals(0, plusMinus("0")) 76 | assertEquals(4, plusMinus("2 + 2")) 77 | assertEquals(6, plusMinus("2 + 31 - 40 + 13")) 78 | assertEquals(-1, plusMinus("0 - 1")) 79 | } 80 | 81 | @Test 82 | @Tag("Hard") 83 | fun firstDuplicateIndex() { 84 | assertEquals(-1, firstDuplicateIndex("Привет")) 85 | assertEquals(9, firstDuplicateIndex("Он пошёл в в школу")) 86 | assertEquals(40, firstDuplicateIndex("Яблоко упало на ветку с ветки оно упало на на землю")) 87 | assertEquals(9, firstDuplicateIndex("Мы пошли прямо Прямо располагался магазин")) 88 | } 89 | 90 | @Test 91 | @Tag("Hard") 92 | fun mostExpensive() { 93 | assertEquals("", mostExpensive("")) 94 | assertEquals("Курица", mostExpensive("Хлеб 39.9; Молоко 62.5; Курица 184.0; Конфеты 89.9")) 95 | assertEquals("Вино", mostExpensive("Вино 255.0")) 96 | } 97 | 98 | @Test 99 | @Tag("Hard") 100 | fun fromRoman() { 101 | assertEquals(1, fromRoman("I")) 102 | assertEquals(3000, fromRoman("MMM")) 103 | assertEquals(1978, fromRoman("MCMLXXVIII")) 104 | assertEquals(694, fromRoman("DCXCIV")) 105 | assertEquals(49, fromRoman("XLIX")) 106 | assertEquals(-1, fromRoman("Z")) 107 | } 108 | 109 | @Test 110 | @Tag("Hard") 111 | fun computeDeviceCells() { 112 | assertEquals(listOf(0, 0, 0, 0, 0, 1, 1, 1, 1, 1), computeDeviceCells(10, "+>+>+>+>+")) 113 | assertEquals(listOf(-1, -1, -1, -1, -1, 0, 0, 0, 0, 0), computeDeviceCells(10, "<-<-<-<-<-")) 114 | assertEquals(listOf(1, 1, 1, 1, 1, 0, 0, 0, 0, 0), computeDeviceCells(10, "- <<<<< +[>+]")) 115 | assertEquals(listOf(0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0), 116 | computeDeviceCells(11, "<<<<< + >>>>>>>>>> --[<-] >+[>+] >++{--< <[<] >+[>+] >++}")) 117 | } 118 | } -------------------------------------------------------------------------------- /src/lesson8/task2/Regex.kt: -------------------------------------------------------------------------------- 1 | package lesson8.task2 2 | 3 | import lesson8.task2.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(val groups: List) { 78 | 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 | 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 | /** 5 | * Пример 6 | * 7 | * Вычисление факториала 8 | */ 9 | fun factorial(n: Int): Double { 10 | var result = 1.0 11 | for (i in 1..n) { 12 | result = result * i // Please do not fix in master 13 | } 14 | return result 15 | } 16 | 17 | /** 18 | * Пример 19 | * 20 | * Проверка числа на простоту -- результат true, если число простое 21 | */ 22 | fun isPrime(n: Int): Boolean { 23 | if (n < 2) return false 24 | for (m in 2..Math.sqrt(n.toDouble()).toInt()) { 25 | if (n % m == 0) return false 26 | } 27 | return true 28 | } 29 | 30 | /** 31 | * Пример 32 | * 33 | * Проверка числа на совершенность -- результат true, если число совершенное 34 | */ 35 | fun isPerfect(n: Int): Boolean { 36 | var sum = 1 37 | for (m in 2..n/2) { 38 | if (n % m > 0) continue 39 | sum += m 40 | if (sum > n) break 41 | } 42 | return sum == n 43 | } 44 | 45 | /** 46 | * Пример 47 | * 48 | * Найти число вхождений цифры m в число n 49 | */ 50 | fun digitCountInNumber(n: Int, m: Int): Int = 51 | if (n == m) 1 else if (n < 10) 0 52 | else digitCountInNumber(n / 10, m) + digitCountInNumber(n % 10, m) 53 | 54 | /** 55 | * Тривиальная 56 | * 57 | * Найти количество цифр в заданном числе n. 58 | * Например, число 1 содержит 1 цифру, 456 -- 3 цифры, 65536 -- 5 цифр. 59 | */ 60 | fun digitNumber(n: Int): Int = TODO() 61 | 62 | /** 63 | * Простая 64 | * 65 | * Найти число Фибоначчи из ряда 1, 1, 2, 3, 5, 8, 13, 21, ... с номером n. 66 | * Ряд Фибоначчи определён следующим образом: fib(1) = 1, fib(2) = 1, fib(n+2) = fib(n) + fib(n+1) 67 | */ 68 | fun fib(n: Int): Int = TODO() 69 | 70 | /** 71 | * Простая 72 | * 73 | * Для заданных чисел m и n найти наименьшее общее кратное, то есть, 74 | * минимальное число k, которое делится и на m и на n без остатка 75 | */ 76 | fun lcm(m: Int, n: Int): Int = TODO() 77 | 78 | /** 79 | * Простая 80 | * 81 | * Для заданного числа n > 1 найти минимальный делитель, превышающий 1 82 | */ 83 | fun minDivisor(n: Int): Int = TODO() 84 | 85 | /** 86 | * Простая 87 | * 88 | * Для заданного числа n > 1 найти максимальный делитель, меньший n 89 | */ 90 | fun maxDivisor(n: Int): Int = TODO() 91 | 92 | /** 93 | * Простая 94 | * 95 | * Определить, являются ли два заданных числа m и n взаимно простыми. 96 | * Взаимно простые числа не имеют общих делителей, кроме 1. 97 | * Например, 25 и 49 взаимно простые, а 6 и 8 -- нет. 98 | */ 99 | fun isCoPrime(m: Int, n: Int): Boolean = TODO() 100 | 101 | /** 102 | * Простая 103 | * 104 | * Для заданных чисел m и n, m <= n, определить, имеется ли хотя бы один точный квадрат между m и n, 105 | * то есть, существует ли такое целое k, что m <= k*k <= n. 106 | * Например, для интервала 21..28 21 <= 5*5 <= 28, а для интервала 51..61 квадрата не существует. 107 | */ 108 | fun squareBetweenExists(m: Int, n: Int): Boolean = TODO() 109 | 110 | /** 111 | * Простая 112 | * 113 | * Для заданного x рассчитать с заданной точностью eps 114 | * sin(x) = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + ... 115 | * Нужную точность считать достигнутой, если очередной член ряда меньше eps по модулю 116 | */ 117 | fun sin(x: Double, eps: Double): Double = TODO() 118 | 119 | /** 120 | * Простая 121 | * 122 | * Для заданного x рассчитать с заданной точностью eps 123 | * cos(x) = 1 - x^2 / 2! + x^4 / 4! - x^6 / 6! + ... 124 | * Нужную точность считать достигнутой, если очередной член ряда меньше eps по модулю 125 | */ 126 | fun cos(x: Double, eps: Double): Double = TODO() 127 | 128 | /** 129 | * Средняя 130 | * 131 | * Поменять порядок цифр заданного числа n на обратный: 13478 -> 87431. 132 | * Не использовать строки при решении задачи. 133 | */ 134 | fun revert(n: Int): Int = TODO() 135 | 136 | /** 137 | * Средняя 138 | * 139 | * Проверить, является ли заданное число n палиндромом: 140 | * первая цифра равна последней, вторая -- предпоследней и так далее. 141 | * 15751 -- палиндром, 3653 -- нет. 142 | */ 143 | fun isPalindrome(n: Int): Boolean = TODO() 144 | 145 | /** 146 | * Средняя 147 | * 148 | * Для заданного числа n определить, содержит ли оно различающиеся цифры. 149 | * Например, 54 и 323 состоят из разных цифр, а 111 и 0 из одинаковых. 150 | */ 151 | fun hasDifferentDigits(n: Int): Boolean = TODO() 152 | 153 | /** 154 | * Сложная 155 | * 156 | * Найти n-ю цифру последовательности из квадратов целых чисел: 157 | * 149162536496481100121144... 158 | * Например, 2-я цифра равна 4, 7-я 5, 12-я 6. 159 | */ 160 | fun squareSequenceDigit(n: Int): Int = TODO() 161 | 162 | /** 163 | * Сложная 164 | * 165 | * Найти n-ю цифру последовательности из чисел Фибоначчи (см. функцию fib выше): 166 | * 1123581321345589144... 167 | * Например, 2-я цифра равна 1, 9-я 2, 14-я 5. 168 | */ 169 | fun fibSequenceDigit(n: Int): Int = TODO() 170 | -------------------------------------------------------------------------------- /src/lesson6/task1/Geometry.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson6.task1 3 | 4 | import lesson1.task1.sqr 5 | 6 | /** 7 | * Точка на плоскости 8 | */ 9 | data class Point(val x: Double, val y: Double) { 10 | /** 11 | * Пример 12 | * 13 | * Рассчитать (по известной формуле) расстояние между двумя точками 14 | */ 15 | fun distance(other: Point): Double = Math.sqrt(sqr(x - other.x) + sqr(y - other.y)) 16 | } 17 | 18 | /** 19 | * Треугольник, заданный тремя точками 20 | */ 21 | data class Triangle(val a: Point, val b: Point, val c: Point) { 22 | /** 23 | * Пример: полупериметр 24 | */ 25 | fun halfPerimeter() = (a.distance(b) + b.distance(c) + c.distance(a)) / 2.0 26 | 27 | /** 28 | * Пример: площадь 29 | */ 30 | fun area(): Double { 31 | val p = halfPerimeter() 32 | return Math.sqrt(p * (p - a.distance(b)) * (p - b.distance(c)) * (p - c.distance(a))) 33 | } 34 | 35 | /** 36 | * Пример: треугольник содержит точку 37 | */ 38 | fun contains(p: Point): Boolean { 39 | val abp = Triangle(a, b, p) 40 | val bcp = Triangle(b, c, p) 41 | val cap = Triangle(c, a, p) 42 | return abp.area() + bcp.area() + cap.area() <= area() 43 | } 44 | } 45 | 46 | /** 47 | * Окружность с заданным центром и радиусом 48 | */ 49 | data class Circle(val center: Point, val radius: Double) { 50 | /** 51 | * Простая 52 | * 53 | * Рассчитать расстояние между двумя окружностями. 54 | * Расстояние между непересекающимися окружностями рассчитывается как 55 | * расстояние между их центрами минус сумма их радиусов. 56 | * Расстояние между пересекающимися окружностями считать равным 0.0. 57 | */ 58 | fun distance(other: Circle): Double = TODO() 59 | 60 | /** 61 | * Тривиальная 62 | * 63 | * Вернуть true, если и только если окружность содержит данную точку НА себе или ВНУТРИ себя 64 | */ 65 | fun contains(p: Point): Boolean = TODO() 66 | } 67 | 68 | /** 69 | * Отрезок между двумя точками 70 | */ 71 | data class Segment(val begin: Point, val end: Point) 72 | 73 | /** 74 | * Средняя 75 | * 76 | * Дано множество точек. Вернуть отрезок, соединяющий две наиболее удалённые из них. 77 | * Если в множестве менее двух точек, бросить IllegalArgumentException 78 | */ 79 | fun diameter(vararg points: Point): Segment = TODO() 80 | 81 | /** 82 | * Простая 83 | * 84 | * Построить окружность по её диаметру, заданному двумя точками 85 | * Центр её должен находиться посередине между точками, а радиус составлять половину расстояния между ними 86 | */ 87 | fun circleByDiameter(diameter: Segment): Circle = TODO() 88 | 89 | /** 90 | * Прямая, заданная точкой и углом наклона (в радианах) по отношению к оси X. 91 | * Уравнение прямой: (y - point.y) * cos(angle) = (x - point.x) * sin(angle) 92 | */ 93 | data class Line(val point: Point, val angle: Double) { 94 | /** 95 | * Средняя 96 | * 97 | * Найти точку пересечения с другой линией. 98 | * Для этого необходимо составить и решить систему из двух уравнений (каждое для своей прямой) 99 | */ 100 | fun crossPoint(other: Line): Point = TODO() 101 | } 102 | 103 | /** 104 | * Средняя 105 | * 106 | * Построить прямую по отрезку 107 | */ 108 | fun lineBySegment(s: Segment): Line = TODO() 109 | 110 | /** 111 | * Средняя 112 | * 113 | * Построить прямую по двум точкам 114 | */ 115 | fun lineByPoints(a: Point, b: Point): Line = TODO() 116 | 117 | /** 118 | * Сложная 119 | * 120 | * Построить серединный перпендикуляр по отрезку или по двум точкам 121 | */ 122 | fun bisectorByPoints(a: Point, b: Point): Line = TODO() 123 | 124 | /** 125 | * Средняя 126 | * 127 | * Задан список из n окружностей на плоскости. Найти пару наименее удалённых из них. 128 | * Если в списке менее двух окружностей, бросить IllegalArgumentException 129 | */ 130 | fun findNearestCirclePair(vararg circles: Circle): Pair = TODO() 131 | 132 | /** 133 | * Очень сложная 134 | * 135 | * Дано три различные точки. Построить окружность, проходящую через них 136 | * (все три точки должны лежать НА, а не ВНУТРИ, окружности). 137 | * Описание алгоритмов см. в Интернете 138 | * (построить окружность по трём точкам, или 139 | * построить окружность, описанную вокруг треугольника - эквивалентная задача). 140 | */ 141 | fun circleByThreePoints(a: Point, b: Point, c: Point): Circle = TODO() 142 | 143 | /** 144 | * Очень сложная 145 | * 146 | * Дано множество точек на плоскости. Найти круг минимального радиуса, 147 | * содержащий все эти точки. Если множество пустое, бросить IllegalArgumentException. 148 | * Если множество содержит одну точку, вернуть круг нулевого радиуса с центром в данной точке. 149 | * 150 | * Примечание: в зависимости от ситуации, такая окружность может либо проходить через какие-либо 151 | * три точки данного множества, либо иметь своим диаметром отрезок, 152 | * соединяющий две самые удалённые точки в данном множестве. 153 | */ 154 | fun minContainingCircle(vararg points: Point): Circle = TODO() 155 | 156 | -------------------------------------------------------------------------------- /test/lesson6/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson6.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 pointDistance() { 11 | assertEquals(0.0, Point(0.0, 0.0).distance(Point(0.0, 0.0)), 1e-5) 12 | assertEquals(5.0, Point(3.0, 0.0).distance(Point(0.0, 4.0)), 1e-5) 13 | assertEquals(50.0, Point(0.0, -30.0).distance(Point(-40.0, 0.0)), 1e-5) 14 | } 15 | 16 | @Test 17 | @Tag("Example") 18 | fun halfPerimeter() { 19 | assertEquals(6.0, Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).halfPerimeter(), 1e-5) 20 | assertEquals(2.0, Triangle(Point(0.0, 0.0), Point(0.0, 1.0), Point(0.0, 2.0)).halfPerimeter(), 1e-5) 21 | } 22 | 23 | @Test 24 | @Tag("Example") 25 | fun triangleArea() { 26 | assertEquals(6.0, Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).area(), 1e-5) 27 | assertEquals(0.0, Triangle(Point(0.0, 0.0), Point(0.0, 1.0), Point(0.0, 2.0)).area(), 1e-5) 28 | } 29 | 30 | @Test 31 | @Tag("Example") 32 | fun triangleContains() { 33 | assertTrue(Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).contains(Point(1.5, 1.5))) 34 | assertFalse(Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).contains(Point(2.5, 2.5))) 35 | } 36 | 37 | @Test 38 | @Tag("Easy") 39 | fun circleDistance() { 40 | assertEquals(0.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(1.0, 0.0), 1.0)), 1e-5) 41 | assertEquals(0.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(0.0, 2.0), 1.0)), 1e-5) 42 | assertEquals(1.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(-4.0, 0.0), 2.0)), 1e-5) 43 | assertEquals(2.0 * Math.sqrt(2.0) - 2.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(2.0, 2.0), 1.0)), 1e-5) 44 | } 45 | 46 | @Test 47 | @Tag("Trivial") 48 | fun circleContains() { 49 | val center = Point(1.0, 2.0) 50 | assertTrue(Circle(center, 1.0).contains(center)) 51 | assertFalse(Circle(center, 2.0).contains(Point(0.0, 0.0))) 52 | assertTrue(Circle(Point(0.0, 3.0), 5.01).contains(Point(-4.0, 0.0))) 53 | } 54 | 55 | @Test 56 | @Tag("Normal") 57 | fun diameter() { 58 | val p1 = Point(0.0, 0.0) 59 | val p2 = Point(1.0, 4.0) 60 | val p3 = Point(-2.0, 2.0) 61 | val p4 = Point(3.0, -1.0) 62 | val p5 = Point(-3.0, -2.0) 63 | val p6 = Point(0.0, 5.0) 64 | assertEquals(Segment(p5, p6), diameter(p1, p2, p3, p4, p5, p6)) 65 | assertEquals(Segment(p4, p6), diameter(p1, p2, p3, p4, p6)) 66 | assertEquals(Segment(p3, p4), diameter(p1, p2, p3, p4)) 67 | assertEquals(Segment(p2, p4), diameter(p1, p2, p4)) 68 | assertEquals(Segment(p1, p4), diameter(p1, p4)) 69 | } 70 | 71 | @Test 72 | @Tag("Easy") 73 | fun circleByDiameter() { 74 | assertEquals(Circle(Point(2.0, 1.5), 2.5), circleByDiameter(Segment(Point(4.0, 0.0), Point(0.0, 3.0)))) 75 | } 76 | 77 | @Test 78 | @Tag("Normal") 79 | fun crossPoint() { 80 | assertTrue(Point(2.0, 3.0).distance(Line(Point(2.0, 0.0), Math.PI / 2).crossPoint(Line(Point(0.0, 3.0), 0.0))) < 1e-5) 81 | assertTrue(Point(2.0, 2.0).distance(Line(Point(0.0, 0.0), Math.PI / 4).crossPoint(Line(Point(0.0, 4.0), -Math.PI / 4))) < 1e-5) 82 | val p = Point(1.0, 3.0) 83 | assertTrue(p.distance(Line(p, 1.0).crossPoint(Line(p, 2.0))) < 1e-5) 84 | } 85 | 86 | @Test 87 | @Tag("Normal") 88 | fun lineBySegment() { 89 | assertEquals(Line(Point(0.0, 0.0), 0.0), lineBySegment(Segment(Point(0.0, 0.0), Point(7.0, 0.0)))) 90 | assertEquals(Line(Point(0.0, 0.0), Math.PI / 2), lineBySegment(Segment(Point(0.0, 0.0), Point(0.0, 8.0)))) 91 | assertEquals(Line(Point(1.0, 1.0), Math.PI / 4), lineBySegment(Segment(Point(1.0, 1.0), Point(3.0, 3.0)))) 92 | } 93 | 94 | @Test 95 | @Tag("Normal") 96 | fun lineByPoint() { 97 | assertEquals(Line(Point(1.0, 1.0), Math.PI / 4), lineByPoints(Point(1.0, 1.0), Point(3.0, 3.0))) 98 | } 99 | 100 | @Test 101 | @Tag("Hard") 102 | fun bisectorByPoints() { 103 | assertEquals(Line(Point(2.0, 0.0), Math.PI / 2), bisectorByPoints(Point(0.0, 0.0), Point(4.0, 0.0))) 104 | assertEquals(Line(Point(1.0, 2.0), 0.0), bisectorByPoints(Point(1.0, 5.0), Point(1.0, -1.0))) 105 | } 106 | 107 | @Test 108 | @Tag("Normal") 109 | fun findNearestCirclePair() { 110 | val c1 = Circle(Point(0.0, 0.0), 1.0) 111 | val c2 = Circle(Point(3.0, 0.0), 5.0) 112 | val c3 = Circle(Point(-5.0, 0.0), 2.0) 113 | val c4 = Circle(Point(0.0, 7.0), 3.0) 114 | val c5 = Circle(Point(0.0, -6.0), 4.0) 115 | assertEquals(Pair(c1, c5), findNearestCirclePair(c1, c3, c4, c5)) 116 | assertEquals(Pair(c2, c4), findNearestCirclePair(c2, c4, c5)) 117 | assertEquals(Pair(c1, c2), findNearestCirclePair(c1, c2, c4, c5)) 118 | } 119 | 120 | @Test 121 | @Tag("Impossible") 122 | fun circleByThreePoints() { 123 | val result = circleByThreePoints(Point(5.0, 0.0), Point(3.0, 4.0), Point(0.0, -5.0)) 124 | assertTrue(result.center.distance(Point(0.0, 0.0)) < 1e-5) 125 | assertEquals(5.0, result.radius, 1e-5) 126 | } 127 | 128 | @Test 129 | @Tag("Impossible") 130 | fun minContainingCircle() { 131 | val p1 = Point(0.0, 0.0) 132 | val p2 = Point(1.0, 4.0) 133 | val p3 = Point(-2.0, 2.0) 134 | val p4 = Point(3.0, -1.0) 135 | val p5 = Point(-3.0, -2.0) 136 | val p6 = Point(0.0, 5.0) 137 | val result = minContainingCircle(p1, p2, p3, p4, p5, p6) 138 | assertEquals(4.0, result.radius, 0.02) 139 | for (p in listOf(p1, p2, p3, p4, p5, p6)) { 140 | assertTrue(result.contains(p)) 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /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 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun factorial() { 11 | assertEquals(1.0, factorial(0), 1e-5) 12 | assertEquals(1.0, factorial(1), 1e-5) 13 | assertEquals(6.0, factorial(3), 1e-5) 14 | assertEquals(120.0, factorial(5), 1e-5) 15 | assertEquals(3628800.0, factorial(10), 1e-5) 16 | assertEquals(2.43290200817664E18, factorial(20), 1E10) 17 | } 18 | 19 | @Test 20 | @Tag("Example") 21 | fun isPrime() { 22 | assertTrue(isPrime(2)) 23 | assertTrue(isPrime(3)) 24 | assertTrue(isPrime(19)) 25 | assertTrue(isPrime(53)) 26 | assertFalse(isPrime(1)) 27 | assertFalse(isPrime(9)) 28 | var count = 0 29 | for (n in 2..7919) { 30 | if (isPrime(n)) { 31 | count++ 32 | } 33 | } 34 | assertEquals(1000, count) 35 | for (n in 2..10000000) { 36 | if (isPrime(n)) { 37 | count++ 38 | } 39 | } 40 | assertEquals(665579, count) 41 | } 42 | 43 | @Test 44 | @Tag("Example") 45 | fun isPerfect() { 46 | assertTrue(isPerfect(6)) 47 | assertTrue(isPerfect(28)) 48 | assertFalse(isPerfect(100)) 49 | } 50 | 51 | @Test 52 | @Tag("Example") 53 | fun digitCountInNumber() { 54 | assertEquals(4, digitCountInNumber(5373393, 3)) 55 | assertEquals(2, digitCountInNumber(100, 0)) 56 | assertEquals(1, digitCountInNumber(0, 0)) 57 | } 58 | 59 | @Test 60 | @Tag("Trivial") 61 | fun digitNumber() { 62 | assertEquals(1, digitNumber(0)) 63 | assertEquals(1, digitNumber(7)) 64 | assertEquals(2, digitNumber(10)) 65 | assertEquals(2, digitNumber(99)) 66 | assertEquals(3, digitNumber(123)) 67 | assertEquals(10, digitNumber(Int.MAX_VALUE)) 68 | } 69 | 70 | @Test 71 | @Tag("Simple") 72 | fun fib() { 73 | assertEquals(1, fib(1)) 74 | assertEquals(1, fib(2)) 75 | assertEquals(2, fib(3)) 76 | assertEquals(5, fib(5)) 77 | assertEquals(21, fib(8)) 78 | assertEquals(102334155, fib(40)) 79 | } 80 | 81 | @Test 82 | @Tag("Easy") 83 | fun lcm() { 84 | assertEquals(13, lcm(13, 13)) 85 | assertEquals(8, lcm(2, 8)) 86 | assertEquals(24, lcm(6, 8)) 87 | assertEquals(975, lcm(39, 75)) 88 | } 89 | 90 | @Test 91 | @Tag("Easy") 92 | fun minDivisor() { 93 | assertEquals(2, minDivisor(2)) 94 | assertEquals(3, minDivisor(75)) 95 | assertEquals(5, minDivisor(75 / 3)) 96 | assertEquals(97, minDivisor(97)) 97 | assertEquals(7, minDivisor(49)) 98 | assertEquals(17, minDivisor(8653)) 99 | } 100 | 101 | @Test 102 | @Tag("Easy") 103 | fun maxDivisor() { 104 | assertEquals(1, maxDivisor(17)) 105 | assertEquals(12, maxDivisor(24)) 106 | assertEquals(59, maxDivisor(177)) 107 | assertEquals(17, maxDivisor(34)) 108 | assertEquals(7, maxDivisor(49)) 109 | } 110 | 111 | @Test 112 | @Tag("Easy") 113 | fun isCoPrime() { 114 | assertTrue(isCoPrime(25, 49)) 115 | assertFalse(isCoPrime(6, 8)) 116 | assertTrue(isCoPrime(17, 97)) 117 | assertFalse(isCoPrime(37, 111)) 118 | } 119 | 120 | @Test 121 | @Tag("Easy") 122 | fun squareBetweenExists() { 123 | assertTrue(squareBetweenExists(1, 1)) 124 | assertTrue(squareBetweenExists(21, 28)) 125 | assertFalse(squareBetweenExists(51, 61)) 126 | assertFalse(squareBetweenExists(999, 1001)) 127 | } 128 | 129 | @Test 130 | @Tag("Easy") 131 | fun sin() { 132 | assertEquals(0.0, sin(0.0, 1e-5), 1e-5) 133 | assertEquals(1.0, sin(Math.PI / 2.0, 1e-5), 1e-5) 134 | assertEquals(0.0, sin(Math.PI, 1e-5), 1e-5) 135 | assertEquals(-1.0, sin(3.0 * Math.PI / 2.0, 1e-5), 1e-5) 136 | } 137 | 138 | @Test 139 | @Tag("Easy") 140 | fun cos() { 141 | assertEquals(1.0, cos(0.0, 1e-5), 1e-5) 142 | assertEquals(0.0, cos(Math.PI / 2.0, 1e-5), 1e-5) 143 | assertEquals(-1.0, cos(Math.PI, 1e-5), 1e-5) 144 | assertEquals(0.0, cos(3.0 * Math.PI / 2.0, 1e-5), 1e-5) 145 | } 146 | 147 | @Test 148 | @Tag("Normal") 149 | fun revert() { 150 | assertEquals(87431, revert(13478)) 151 | assertEquals(0, revert(0)) 152 | assertEquals(3, revert(3)) 153 | assertEquals(111, revert(111)) 154 | assertEquals(17571, revert(17571)) 155 | assertEquals(123456789, revert(987654321)) 156 | } 157 | 158 | @Test 159 | @Tag("Normal") 160 | fun isPalindrome() { 161 | assertTrue(isPalindrome(3)) 162 | assertFalse(isPalindrome(3653)) 163 | assertTrue(isPalindrome(15751)) 164 | assertTrue(isPalindrome(24688642)) 165 | } 166 | 167 | @Test 168 | @Tag("Normal") 169 | fun hasDifferentDigits() { 170 | assertTrue(hasDifferentDigits(323)) 171 | assertTrue(hasDifferentDigits(54)) 172 | assertTrue(hasDifferentDigits(222266666)) 173 | assertFalse(hasDifferentDigits(0)) 174 | assertFalse(hasDifferentDigits(777)) 175 | } 176 | 177 | @Test 178 | @Tag("Hard") 179 | fun squareSequenceDigit() { 180 | assertEquals(1, squareSequenceDigit(1)) 181 | assertEquals(4, squareSequenceDigit(2)) 182 | assertEquals(5, squareSequenceDigit(7)) 183 | assertEquals(6, squareSequenceDigit(12)) 184 | assertEquals(0, squareSequenceDigit(17)) 185 | assertEquals(9, squareSequenceDigit(27)) 186 | } 187 | 188 | @Test 189 | @Tag("Hard") 190 | fun fibSequenceDigit() { 191 | assertEquals(1, fibSequenceDigit(1)) 192 | assertEquals(1, fibSequenceDigit(2)) 193 | assertEquals(3, fibSequenceDigit(4)) 194 | assertEquals(2, fibSequenceDigit(9)) 195 | assertEquals(5, fibSequenceDigit(14)) 196 | assertEquals(2, fibSequenceDigit(20)) 197 | } 198 | } -------------------------------------------------------------------------------- /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 | assertEquals(listOf(0.0), sqRoots(0.0)) 13 | assertEquals(listOf(-5.0, 5.0), sqRoots(25.0)) 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 | assertEquals(listOf(-2.0, 2.0), biRoots(0.0, 1.0, -4.0)) 22 | assertEquals(listOf(), biRoots(1.0, -2.0, 4.0)) 23 | assertEquals(listOf(-1.0, 1.0), biRoots(1.0, -2.0, 1.0)) 24 | assertEquals(listOf(), biRoots(1.0, 3.0, 2.0)) 25 | assertEquals(listOf(-2.0, -1.0, 1.0, 2.0), biRoots(1.0, -5.0, 4.0).sorted()) 26 | } 27 | 28 | @Test 29 | @Tag("Example") 30 | fun negativeList() { 31 | assertEquals(listOf(), negativeList(listOf(1, 2, 3))) 32 | assertEquals(listOf(-1, -5), negativeList(listOf(-1, 2, 4, -5))) 33 | } 34 | 35 | @Test 36 | @Tag("Example") 37 | fun invertPositives() { 38 | val list1 = mutableListOf(1, 2, 3) 39 | invertPositives(list1) 40 | assertEquals(listOf(-1, -2, -3), list1) 41 | val list2 = mutableListOf(-1, 2, 4, -5) 42 | invertPositives(list2) 43 | assertEquals(listOf(-1, -2, -4, -5), list2) 44 | } 45 | 46 | @Test 47 | @Tag("Example") 48 | fun squares() { 49 | assertEquals(listOf(0), squares(listOf(0))) 50 | assertEquals(listOf(1, 4, 9), squares(listOf(1, 2, -3))) 51 | } 52 | 53 | @Test 54 | @Tag("Example") 55 | fun isPalindrome() { 56 | assertFalse(isPalindrome("Барабан")) 57 | assertTrue(isPalindrome("А роза упала на лапу Азора")) 58 | assertTrue(isPalindrome("Шалаш")) 59 | } 60 | 61 | @Test 62 | @Tag("Example") 63 | fun buildSumExample() { 64 | assertEquals("42 = 42", buildSumExample(listOf(42))) 65 | assertEquals("3 + 6 + 5 + 4 + 9 = 27", buildSumExample(listOf(3, 6, 5, 4, 9))) 66 | } 67 | 68 | @Test 69 | @Tag("Easy") 70 | fun abs() { 71 | assertEquals(0.0, abs(listOf()), 1e-5) 72 | assertEquals(3.0, abs(listOf(3.0)), 1e-5) 73 | assertEquals(5.0, abs(listOf(3.0, -4.0)), 1e-5) 74 | assertEquals(8.774964, abs(listOf(4.0, -5.0, 6.0)), 1e-5) 75 | } 76 | 77 | @Test 78 | @Tag("Easy") 79 | fun mean() { 80 | assertEquals(0.0, mean(listOf()), 1e-5) 81 | assertEquals(1.0, mean(listOf(1.0)), 1e-5) 82 | assertEquals(2.0, mean(listOf(3.0, 1.0, 2.0)), 1e-5) 83 | assertEquals(3.0, mean(listOf(0.0, 2.0, 7.0, 8.0, -2.0)), 1e-5) 84 | } 85 | 86 | @Test 87 | @Tag("Normal") 88 | fun center() { 89 | assertEquals(listOf(), center(mutableListOf())) 90 | assertEquals(listOf(0.0), center(mutableListOf(3.14))) 91 | assertEquals(listOf(1.0, -1.0, 0.0), center(mutableListOf(3.0, 1.0, 2.0))) 92 | assertEquals(listOf(-3.0, -1.0, 4.0, 5.0, -5.0), center(mutableListOf(0.0, 2.0, 7.0, 8.0, -2.0))) 93 | } 94 | 95 | @Test 96 | @Tag("Normal") 97 | fun times() { 98 | assertEquals(0.0, times(listOf(), listOf()), 1e-5) 99 | assertEquals(-5.0, times(listOf(1.0, -4.0), listOf(3.0, 2.0)), 1e-5) 100 | assertEquals(-19.0, times(listOf(-1.0, 2.0, -3.0), listOf(3.0, -2.0, 4.0)), 1e-5) 101 | } 102 | 103 | @Test 104 | @Tag("Normal") 105 | fun polynom() { 106 | assertEquals(0.0, polynom(listOf(), 1000.0), 1e-5) 107 | assertEquals(42.0, polynom(listOf(42.0), -1000.0), 1e-5) 108 | assertEquals(13.0, polynom(listOf(3.0, 2.0), 5.0), 1e-5) 109 | assertEquals(0.0, polynom(listOf(2.0, -3.0, 1.0), 1.0), 1e-5) 110 | assertEquals(45.0, polynom(listOf(-7.0, 6.0, 4.0, -4.0, 1.0), -2.0), 1e-5) 111 | } 112 | 113 | @Test 114 | @Tag("Normal") 115 | fun accumulate() { 116 | assertEquals(listOf(), accumulate(mutableListOf())) 117 | assertEquals(listOf(3.14), accumulate(mutableListOf(3.14))) 118 | assertEquals(listOf(1.0, 3.0, 6.0, 10.0), accumulate(mutableListOf(1.0, 2.0, 3.0, 4.0))) 119 | } 120 | 121 | @Test 122 | @Tag("Normal") 123 | fun factorize() { 124 | assertEquals(listOf(2), factorize(2)) 125 | assertEquals(listOf(3, 5, 5), factorize(75)) 126 | assertEquals(listOf(2, 3, 3, 19), factorize(342)) 127 | } 128 | 129 | @Test 130 | @Tag("Hard") 131 | fun factorizeToString() { 132 | assertEquals("2", factorizeToString(2)) 133 | assertEquals("3*5*5", factorizeToString(75)) 134 | assertEquals("2*3*3*19", factorizeToString(342)) 135 | } 136 | 137 | @Test 138 | @Tag("Normal") 139 | fun convert() { 140 | assertEquals(listOf(1), convert(1, 2)) 141 | assertEquals(listOf(1, 2, 1, 0), convert(100, 4)) 142 | assertEquals(listOf(1, 3, 12), convert(250, 14)) 143 | assertEquals(listOf(2, 14, 12), convert(1000, 19)) 144 | } 145 | 146 | @Test 147 | @Tag("Hard") 148 | fun convertToString() { 149 | assertEquals("1", convertToString(1, 2)) 150 | assertEquals("1210", convertToString(100, 4)) 151 | assertEquals("13c", convertToString(250, 14)) 152 | assertEquals("2ec", convertToString(1000, 19)) 153 | assertEquals("z", convertToString(35, 36)) 154 | } 155 | 156 | @Test 157 | @Tag("Normal") 158 | fun decimal() { 159 | assertEquals(1, decimal(listOf(1), 2)) 160 | assertEquals(100, decimal(listOf(1, 2, 1, 0), 4)) 161 | assertEquals(250, decimal(listOf(1, 3, 12), 14)) 162 | assertEquals(1000, decimal(listOf(2, 14, 12), 19)) 163 | } 164 | 165 | @Test 166 | @Tag("Hard") 167 | fun decimalFromString() { 168 | assertEquals(1, decimalFromString("1", 2)) 169 | assertEquals(100, decimalFromString("1210", 4)) 170 | assertEquals(250, decimalFromString("13c", 14)) 171 | assertEquals(1000, decimalFromString("2ec", 19)) 172 | assertEquals(35, decimalFromString("z", 36)) 173 | } 174 | 175 | @Test 176 | @Tag("Hard") 177 | fun roman() { 178 | assertEquals("I", roman(1)) 179 | assertEquals("MMM", roman(3000)) 180 | assertEquals("MCMLXXVIII", roman(1978)) 181 | assertEquals("DCXCIV", roman(694)) 182 | assertEquals("XLIX", roman(49)) 183 | } 184 | 185 | @Test 186 | @Tag("Impossible") 187 | fun russian() { 188 | assertEquals("триста семьдесят пять", russian(375)) 189 | assertEquals("двадцать две тысячи девятьсот шестьдесят четыре", russian(22964)) 190 | assertEquals("сто девятнадцать тысяч пятьсот восемь", russian(119508)) 191 | assertEquals("две тысячи три", russian(2003)) 192 | assertEquals("двести тысяч два", russian(200002)) 193 | assertEquals("девятьсот тысяч", russian(900000)) 194 | assertEquals("двенадцать", russian(12)) 195 | } 196 | } -------------------------------------------------------------------------------- /src/lesson4/task1/List.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson4.task1 3 | 4 | import lesson1.task1.discriminant 5 | 6 | /** 7 | * Пример 8 | * 9 | * Найти все корни уравнения x^2 = y 10 | */ 11 | fun sqRoots(y: Double) = 12 | if (y < 0) listOf() 13 | else if (y == 0.0) listOf(0.0) 14 | else { 15 | val root = Math.sqrt(y) 16 | // Результат! 17 | listOf(-root, root) 18 | } 19 | 20 | /** 21 | * Пример 22 | * 23 | * Найти все корни биквадратного уравнения ax^4 + bx^2 + c = 0. 24 | * Вернуть список корней (пустой, если корней нет) 25 | */ 26 | fun biRoots(a: Double, b: Double, c: Double): List { 27 | if (a == 0.0) { 28 | if (b == 0.0) return listOf() 29 | else return sqRoots(-c / b) 30 | } 31 | val d = discriminant(a, b, c) 32 | if (d < 0.0) return listOf() 33 | if (d == 0.0) return sqRoots(-b / (2 * a)) 34 | val y1 = (-b + Math.sqrt(d)) / (2 * a) 35 | val y2 = (-b - Math.sqrt(d)) / (2 * a) 36 | return sqRoots(y1) + sqRoots(y2) 37 | } 38 | 39 | /** 40 | * Пример 41 | * 42 | * Выделить в список отрицательные элементы из заданного списка 43 | */ 44 | fun negativeList(list: List): List { 45 | val result = mutableListOf() 46 | for (element in list) { 47 | if (element < 0) { 48 | result.add(element) 49 | } 50 | } 51 | return result 52 | } 53 | 54 | /** 55 | * Пример 56 | * 57 | * Изменить знак для всех положительных элементов списка 58 | */ 59 | fun invertPositives(list: MutableList) { 60 | for (i in 0..list.size - 1) { 61 | val element = list[i] 62 | if (element > 0) { 63 | list[i] = -element 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Пример 70 | * 71 | * Из имеющегося списка целых чисел, сформировать список их квадратов 72 | */ 73 | fun squares(list: List) = list.map { it * it } 74 | 75 | /** 76 | * Пример 77 | * 78 | * По заданной строке str определить, является ли она палиндромом. 79 | * В палиндроме первый символ должен быть равен последнему, второй предпоследнему и т.д. 80 | * Одни и те же буквы в разном регистре следует считать равными с точки зрения данной задачи. 81 | * Пробелы не следует принимать во внимание при сравнении символов, например, строка 82 | * "А роза упала на лапу Азора" является палиндромом. 83 | */ 84 | fun isPalindrome(str: String): Boolean { 85 | val lowerCase = str.toLowerCase().filter { it != ' ' } 86 | for (i in 0..lowerCase.length / 2) { 87 | if (lowerCase[i] != lowerCase[lowerCase.length - i - 1]) return false 88 | } 89 | return true 90 | } 91 | 92 | /** 93 | * Пример 94 | * 95 | * По имеющемуся списку целых чисел, например [3, 6, 5, 4, 9], построить строку с примером их суммирования: 96 | * 3 + 6 + 5 + 4 + 9 = 27 в данном случае. 97 | */ 98 | fun buildSumExample(list: List) = list.joinToString(separator = " + ", postfix = " = ${list.sum()}") 99 | 100 | /** 101 | * Простая 102 | * 103 | * Найти модуль заданного вектора, представленного в виде списка v, 104 | * по формуле abs = sqrt(a1^2 + a2^2 + ... + aN^2). 105 | * Модуль пустого вектора считать равным 0.0. 106 | */ 107 | fun abs(v: List): Double = TODO() 108 | 109 | /** 110 | * Простая 111 | * 112 | * Рассчитать среднее арифметическое элементов списка list. Вернуть 0.0, если список пуст 113 | */ 114 | fun mean(list: List): Double = TODO() 115 | 116 | /** 117 | * Средняя 118 | * 119 | * Центрировать заданный список list, уменьшив каждый элемент на среднее арифметическое всех элементов. 120 | * Если список пуст, не делать ничего. Вернуть изменённый список. 121 | */ 122 | fun center(list: MutableList): MutableList = TODO() 123 | 124 | /** 125 | * Средняя 126 | * 127 | * Найти скалярное произведение двух векторов равной размерности, 128 | * представленные в виде списков a и b. Скалярное произведение считать по формуле: 129 | * C = a1b1 + a2b2 + ... + aNbN. Произведение пустых векторов считать равным 0.0. 130 | */ 131 | fun times(a: List, b: List): Double = TODO() 132 | 133 | /** 134 | * Средняя 135 | * 136 | * Рассчитать значение многочлена при заданном x: 137 | * p(x) = p0 + p1*x + p2*x^2 + p3*x^3 + ... + pN*x^N. 138 | * Коэффициенты многочлена заданы списком p: (p0, p1, p2, p3, ..., pN). 139 | * Значение пустого многочлена равно 0.0 при любом x. 140 | */ 141 | fun polynom(p: List, x: Double): Double = TODO() 142 | 143 | /** 144 | * Средняя 145 | * 146 | * В заданном списке list каждый элемент, кроме первого, заменить 147 | * суммой данного элемента и всех предыдущих. 148 | * Например: 1, 2, 3, 4 -> 1, 3, 6, 10. 149 | * Пустой список не следует изменять. Вернуть изменённый список. 150 | */ 151 | fun accumulate(list: MutableList): MutableList = TODO() 152 | 153 | /** 154 | * Средняя 155 | * 156 | * Разложить заданное натуральное число n > 1 на простые множители. 157 | * Результат разложения вернуть в виде списка множителей, например 75 -> (3, 5, 5). 158 | * Множители в списке должны располагаться по возрастанию. 159 | */ 160 | fun factorize(n: Int): List = TODO() 161 | 162 | /** 163 | * Сложная 164 | * 165 | * Разложить заданное натуральное число n > 1 на простые множители. 166 | * Результат разложения вернуть в виде строки, например 75 -> 3*5*5 167 | */ 168 | fun factorizeToString(n: Int): String = TODO() 169 | 170 | /** 171 | * Средняя 172 | * 173 | * Перевести заданное натуральное число n в систему счисления с основанием base > 1. 174 | * Результат перевода вернуть в виде списка цифр в base-ичной системе от старшей к младшей, 175 | * например: n = 100, base = 4 -> (1, 2, 1, 0) или n = 250, base = 14 -> (1, 3, 12) 176 | */ 177 | fun convert(n: Int, base: Int): List = TODO() 178 | 179 | /** 180 | * Сложная 181 | * 182 | * Перевести заданное натуральное число n в систему счисления с основанием 1 < base < 37. 183 | * Результат перевода вернуть в виде строки, цифры более 9 представлять латинскими 184 | * строчными буквами: 10 -> a, 11 -> b, 12 -> c и так далее. 185 | * Например: n = 100, base = 4 -> 1210, n = 250, base = 14 -> 13c 186 | */ 187 | fun convertToString(n: Int, base: Int): String = TODO() 188 | 189 | /** 190 | * Средняя 191 | * 192 | * Перевести число, представленное списком цифр digits от старшей к младшей, 193 | * из системы счисления с основанием base в десятичную. 194 | * Например: digits = (1, 3, 12), base = 14 -> 250 195 | */ 196 | fun decimal(digits: List, base: Int): Int = TODO() 197 | 198 | /** 199 | * Сложная 200 | * 201 | * Перевести число, представленное цифровой строкой str, 202 | * из системы счисления с основанием base в десятичную. 203 | * Цифры более 9 представляются латинскими строчными буквами: 204 | * 10 -> a, 11 -> b, 12 -> c и так далее. 205 | * Например: str = "13c", base = 14 -> 250 206 | */ 207 | fun decimalFromString(str: String, base: Int): Int = TODO() 208 | 209 | /** 210 | * Сложная 211 | * 212 | * Перевести натуральное число n в римскую систему. 213 | * Римские цифры: 1 = I, 4 = IV, 5 = V, 9 = IX, 10 = X, 40 = XL, 50 = L, 214 | * 90 = XC, 100 = C, 400 = CD, 500 = D, 900 = CM, 1000 = M. 215 | * Например: 23 = XXIII, 44 = XLIV, 100 = C 216 | */ 217 | fun roman(n: Int): String = TODO() 218 | 219 | /** 220 | * Очень сложная 221 | * 222 | * Записать заданное натуральное число 1..999999 прописью по-русски. 223 | * Например, 375 = "триста семьдесят пять", 224 | * 23964 = "двадцать три тысячи девятьсот шестьдесят четыре" 225 | */ 226 | fun russian(n: Int): String = TODO() -------------------------------------------------------------------------------- /src/lesson5/task1/Parse.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson5.task1 3 | 4 | /** 5 | * Пример 6 | * 7 | * Время представлено строкой вида "11:34:45", содержащей часы, минуты и секунды, разделённые двоеточием. 8 | * Разобрать эту строку и рассчитать количество секунд, прошедшее с начала дня. 9 | */ 10 | fun timeStrToSeconds(str: String): Int { 11 | val parts = str.split(":") 12 | var result = 0 13 | for (part in parts) { 14 | val number = part.toInt() 15 | result = result * 60 + number 16 | } 17 | return result 18 | } 19 | 20 | fun twoDigitStr(n: Int) = if (n in 0..9) "0$n" else "$n" 21 | 22 | /** 23 | * Пример 24 | * 25 | * Дано seconds -- время в секундах, прошедшее с начала дня. 26 | * Вернуть текущее время в виде строки в формате "ЧЧ:ММ:СС". 27 | */ 28 | fun timeSecondsToStr(seconds: Int): String { 29 | val hour = seconds / 3600 30 | val minute = (seconds % 3600) / 60 31 | val second = seconds % 60 32 | return String.format("%02d:%02d:%02d", hour, minute, second) 33 | } 34 | 35 | /** 36 | * Пример: консольный ввод 37 | */ 38 | fun main(args: Array) { 39 | println("Введите время в формате ЧЧ:ММ:СС") 40 | val line = readLine() 41 | if (line != null) { 42 | val seconds = timeStrToSeconds(line) 43 | if (seconds == -1) { 44 | println("Введённая строка $line не соответствует формату ЧЧ:ММ:СС") 45 | } 46 | else { 47 | println("Прошло секунд с начала суток: $seconds") 48 | } 49 | } 50 | else { 51 | println("Достигнут <конец файла> в процессе чтения строки. Программа прервана") 52 | } 53 | } 54 | 55 | /** 56 | * Средняя 57 | * 58 | * Дата представлена строкой вида "15 июля 2016". 59 | * Перевести её в цифровой формат "15.07.2016". 60 | * День и месяц всегда представлять двумя цифрами, например: 03.04.2011. 61 | * При неверном формате входной строки вернуть пустую строку 62 | */ 63 | fun dateStrToDigit(str: String): String = TODO() 64 | 65 | /** 66 | * Средняя 67 | * 68 | * Дата представлена строкой вида "15.07.2016". 69 | * Перевести её в строковый формат вида "15 июля 2016". 70 | * При неверном формате входной строки вернуть пустую строку 71 | */ 72 | fun dateDigitToStr(digital: String): String = TODO() 73 | 74 | /** 75 | * Сложная 76 | * 77 | * Номер телефона задан строкой вида "+7 (921) 123-45-67". 78 | * Префикс (+7) может отсутствовать, код города (в скобках) также может отсутствовать. 79 | * Может присутствовать неограниченное количество пробелов и чёрточек, 80 | * например, номер 12 -- 34- 5 -- 67 -98 тоже следует считать легальным. 81 | * Перевести номер в формат без скобок, пробелов и чёрточек (но с +), например, 82 | * "+79211234567" или "123456789" для приведённых примеров. 83 | * Все символы в номере, кроме цифр, пробелов и +-(), считать недопустимыми. 84 | * При неверном формате вернуть пустую строку 85 | */ 86 | fun flattenPhoneNumber(phone: String): String = TODO() 87 | 88 | /** 89 | * Средняя 90 | * 91 | * Результаты спортсмена на соревнованиях в прыжках в длину представлены строкой вида 92 | * "706 - % 717 % 703". 93 | * В строке могут присутствовать числа, черточки - и знаки процента %, разделённые пробелами; 94 | * число соответствует удачному прыжку, - пропущенной попытке, % заступу. 95 | * Прочитать строку и вернуть максимальное присутствующее в ней число (717 в примере). 96 | * При нарушении формата входной строки или при отсутствии в ней чисел, вернуть -1. 97 | */ 98 | fun bestLongJump(jumps: String): Int = TODO() 99 | 100 | /** 101 | * Сложная 102 | * 103 | * Результаты спортсмена на соревнованиях в прыжках в высоту представлены строкой вида 104 | * "220 + 224 %+ 228 %- 230 + 232 %%- 234 %". 105 | * Здесь + соответствует удачной попытке, % неудачной, - пропущенной. 106 | * Высота и соответствующие ей попытки разделяются пробелом. 107 | * Прочитать строку и вернуть максимальную взятую высоту (230 в примере). 108 | * При нарушении формата входной строки вернуть -1. 109 | */ 110 | fun bestHighJump(jumps: String): Int = TODO() 111 | 112 | /** 113 | * Сложная 114 | * 115 | * В строке представлено выражение вида "2 + 31 - 40 + 13", 116 | * использующее целые положительные числа, плюсы и минусы, разделённые пробелами. 117 | * Наличие двух знаков подряд "13 + + 10" или двух чисел подряд "1 2" не допускается. 118 | * Вернуть значение выражения (6 для примера). 119 | * Про нарушении формата входной строки бросить исключение IllegalArgumentException 120 | */ 121 | fun plusMinus(expression: String): Int = TODO() 122 | 123 | /** 124 | * Сложная 125 | * 126 | * Строка состоит из набора слов, отделённых друг от друга одним пробелом. 127 | * Определить, имеются ли в строке повторяющиеся слова, идущие друг за другом. 128 | * Слова, отличающиеся только регистром, считать совпадающими. 129 | * Вернуть индекс начала первого повторяющегося слова, или -1, если повторов нет. 130 | * Пример: "Он пошёл в в школу" => результат 9 (индекс первого 'в') 131 | */ 132 | fun firstDuplicateIndex(str: String): Int = TODO() 133 | 134 | /** 135 | * Сложная 136 | * 137 | * Строка содержит названия товаров и цены на них в формате вида 138 | * "Хлеб 39.9; Молоко 62.5; Курица 184.0; Конфеты 89.9". 139 | * То есть, название товара отделено от цены пробелом, 140 | * а цена отделена от названия следующего товара точкой с запятой и пробелом. 141 | * Вернуть название самого дорогого товара в списке (в примере это Курица), 142 | * или пустую строку при нарушении формата строки. 143 | * Все цены должны быть положительными 144 | */ 145 | fun mostExpensive(description: String): String = TODO() 146 | 147 | /** 148 | * Сложная 149 | * 150 | * Перевести число roman, заданное в римской системе счисления, 151 | * в десятичную систему и вернуть как результат. 152 | * Римские цифры: 1 = I, 4 = IV, 5 = V, 9 = IX, 10 = X, 40 = XL, 50 = L, 153 | * 90 = XC, 100 = C, 400 = CD, 500 = D, 900 = CM, 1000 = M. 154 | * Например: XXIII = 23, XLIV = 44, C = 100 155 | * 156 | * Вернуть -1, если roman не является корректным римским числом 157 | */ 158 | fun fromRoman(roman: String): Int = TODO() 159 | 160 | /** 161 | * Сложная 162 | * 163 | * Имеется специальное устройство, представляющее собой 164 | * конвейер из cells ячеек (нумеруются от 0 до cells - 1 слева направо) и датчик, двигающийся над этим конвейером. 165 | * Строка commands содержит последовательность команд, выполняемых данным устройством, например +>+>+>+>+ 166 | * Каждая команда кодируется одним специальным символом: 167 | * > - сдвиг датчика вправо на 1 ячейку; 168 | * < - сдвиг датчика влево на 1 ячейку; 169 | * + - увеличение значения в ячейке под датчиком на 1 ед.; 170 | * - - уменьшение значения в ячейке под датчиком на 1 ед.; 171 | * [ - если значение под датчиком равно 0, в качестве следующей команды следует воспринимать 172 | * не следующую по порядку, а идущую за следующей командой ']'; 173 | * ] - если значение под датчиком не равно 0, в качестве следующей команды следует воспринимать 174 | * не следующую по порядку, а идущую за предыдущей командой '['; 175 | * { - если значение под датчиком равно 0, в качестве следующей команды следует воспринимать 176 | * не следующую по порядку, а идущую за следующей командой '}'; 177 | * } - если значение под датчиком не равно 0, в качестве следующей команды следует воспринимать 178 | * не следующую по порядку, а идущую за предыдущей командой '{'; 179 | * (комбинации [] и {} имитируют циклы) 180 | * 181 | * Пробел кодирует пустую команду. 182 | * Все прочие символы следует считать ошибочными и формировать исключение IllegalArgumentException. 183 | * То же исключение формируется, если у символов [ ] { } не оказывается пары. 184 | * Выход за границу конвейера также следует считать ошибкой и формировать исключение IllegalStateException. 185 | * Изначально все ячейки заполнены значением 0 и датчик стоит на ячейке с номером N/2 (округлять вниз) 186 | * 187 | * Вернуть список размера cells, содержащий элементы ячеек устройства после выполнения всех команд. 188 | * Например, для 10 ячеек и командной строки +>+>+>+>+ результат должен быть 0,0,0,0,0,1,1,1,1,1 189 | */ 190 | fun computeDeviceCells(cells: Int, commands: String): List = TODO() -------------------------------------------------------------------------------- /test/lesson6/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson6.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 inside() { 11 | assertTrue(Square(1, 1).inside()) 12 | assertTrue(Square(8, 8).inside()) 13 | assertTrue(Square(1, 8).inside()) 14 | assertFalse(Square(0, 0).inside()) 15 | assertFalse(Square(0, 1).inside()) 16 | assertFalse(Square(9, 4).inside()) 17 | assertFalse(Square(6, 9).inside()) 18 | assertFalse(Square(100, 1).inside()) 19 | assertFalse(Square(7, -100).inside()) 20 | } 21 | 22 | @Test 23 | @Tag("Easy") 24 | fun notation() { 25 | assertEquals("", Square(1, 0).notation()) 26 | assertEquals("b3", Square(2, 3).notation()) 27 | assertEquals("g6", Square(7, 6).notation()) 28 | assertEquals("a8", Square(1, 8).notation()) 29 | assertEquals("h1", Square(8, 1).notation()) 30 | } 31 | 32 | @Test 33 | @Tag("Easy") 34 | fun square() { 35 | assertEquals(Square(3, 2), square("c2")) 36 | assertEquals(Square(5, 5), square("e5")) 37 | assertEquals(Square(6, 8), square("f8")) 38 | assertEquals(Square(4, 1), square("d1")) 39 | } 40 | 41 | @Test 42 | @Tag("Easy") 43 | fun rookMoveNumber() { 44 | assertEquals(0, rookMoveNumber(square("e3"), square("e3"))) 45 | assertEquals(2, rookMoveNumber(square("c2"), square("b1"))) 46 | assertEquals(2, rookMoveNumber(square("g8"), square("f6"))) 47 | assertEquals(1, rookMoveNumber(square("a8"), square("g8"))) 48 | assertEquals(1, rookMoveNumber(square("h3"), square("h8"))) 49 | } 50 | 51 | private fun List.assertRookTrajectory(start: Square, end: Square, length: Int) { 52 | assertEquals(length + 1, size) 53 | assertEquals(start, first()) 54 | assertEquals(end, last()) 55 | for (i in 0..size - 2) { 56 | val previous = this[i] 57 | val next = this[i + 1] 58 | assertTrue(previous.row == next.row || previous.column == next.column) 59 | } 60 | } 61 | 62 | @Test 63 | @Tag("Normal") 64 | fun rookTrajectory() { 65 | assertEquals(listOf(square("g5")), rookTrajectory(square("g5"), square("g5"))) 66 | rookTrajectory(square("c3"), square("h6")).assertRookTrajectory(square("c3"), square("h6"), 2) 67 | assertEquals(listOf(square("h2"), square("h7")), rookTrajectory(square("h2"), square("h7"))) 68 | } 69 | 70 | @Test 71 | @Tag("Easy") 72 | fun bishopMoveNumber() { 73 | assertEquals(-1, bishopMoveNumber(square("a1"), square("g8"))) 74 | assertEquals(-1, bishopMoveNumber(square("c1"), square("f3"))) 75 | assertEquals(0, bishopMoveNumber(square("d4"), square("d4"))) 76 | assertEquals(1, bishopMoveNumber(square("a3"), square("e7"))) 77 | assertEquals(2, bishopMoveNumber(square("c1"), square("c7"))) 78 | } 79 | 80 | private fun List.assertBishopTrajectory(start: Square, end: Square, length: Int) { 81 | assertEquals(length + 1, size) 82 | assertEquals(start, first()) 83 | assertEquals(end, last()) 84 | for (i in 0..size - 2) { 85 | val previous = this[i] 86 | val next = this[i + 1] 87 | assertTrue(Math.abs(next.row - previous.row) == Math.abs(next.column - previous.column)) 88 | } 89 | } 90 | 91 | @Test 92 | @Tag("Hard") 93 | fun bishopTrajectory() { 94 | assertEquals(listOf(), bishopTrajectory(square("a1"), square("g8"))) 95 | assertEquals(listOf(), bishopTrajectory(square("c1"), square("f3"))) 96 | assertEquals(listOf(square("d4")), bishopTrajectory(square("d4"), square("d4"))) 97 | assertEquals(listOf(square("a3"), square("e7")), bishopTrajectory(square("a3"), square("e7"))) 98 | assertEquals(listOf(square("c1"), square("f4"), square("c7")), bishopTrajectory(square("c1"), square("c7"))) 99 | assertEquals(listOf(square("f1"), square("c4"), square("f7")), bishopTrajectory(square("f1"), square("f7"))) 100 | bishopTrajectory(square("d2"), square("e5")).assertBishopTrajectory(square("d2"), square("e5"), 2) 101 | } 102 | 103 | @Test 104 | @Tag("Normal") 105 | fun kingMoveNumber() { 106 | assertEquals(0, kingMoveNumber(square("e3"), square("e3"))) 107 | assertEquals(1, kingMoveNumber(square("c2"), square("b1"))) 108 | assertEquals(2, kingMoveNumber(square("g8"), square("f6"))) 109 | assertEquals(6, kingMoveNumber(square("a8"), square("g8"))) 110 | assertEquals(7, kingMoveNumber(square("a1"), square("h8"))) 111 | } 112 | 113 | private fun List.assertKingTrajectory(start: Square, end: Square, length: Int) { 114 | assertEquals(length + 1, size) 115 | assertEquals(start, first()) 116 | assertEquals(end, last()) 117 | for (i in 0..size - 2) { 118 | val previous = this[i] 119 | val next = this[i + 1] 120 | assertTrue(Math.abs(next.column - previous.column) <= 1) 121 | assertTrue(Math.abs(next.row - previous.row) <= 1) 122 | } 123 | } 124 | 125 | @Test 126 | @Tag("Hard") 127 | fun kingTrajectory() { 128 | assertEquals(listOf(square("f3")), kingTrajectory(square("f3"), square("f3"))) 129 | kingTrajectory(square("c2"), square("a6")).assertKingTrajectory(square("c2"), square("a6"), 4) 130 | assertEquals(listOf(square("h2"), square("g3"), square("f4"), square("e5"), square("d6"), square("c7")), 131 | kingTrajectory(square("h2"), square("c7"))) 132 | } 133 | 134 | @Test 135 | @Tag("Hard") 136 | fun knightMoveNumber() { 137 | assertEquals(0, knightMoveNumber(square("d3"), square("d3"))) 138 | assertEquals(1, knightMoveNumber(square("e4"), square("d6"))) 139 | assertEquals(2, knightMoveNumber(square("f5"), square("g6"))) 140 | assertEquals(3, knightMoveNumber(square("g6"), square("g3"))) 141 | assertEquals(3, knightMoveNumber(square("d4"), square("a8"))) 142 | assertEquals(4, knightMoveNumber(square("h7"), square("f5"))) 143 | assertEquals(4, knightMoveNumber(square("g7"), square("h8"))) 144 | assertEquals(6, knightMoveNumber(square("a8"), square("h1"))) 145 | } 146 | 147 | private fun List.assertKnightTrajectory(start: Square, end: Square, length: Int) { 148 | assertEquals(length + 1, size) 149 | assertEquals(start, first()) 150 | assertEquals(end, last()) 151 | for (i in 0..size - 2) { 152 | val previous = this[i] 153 | val next = this[i + 1] 154 | assertTrue( 155 | Math.abs(next.column - previous.column) == 2 && Math.abs(next.row - previous.row) == 1 || 156 | Math.abs(next.column - previous.column) == 1 && Math.abs(next.row - previous.row) == 2 157 | ) 158 | } 159 | } 160 | 161 | @Test 162 | @Tag("Impossible") 163 | fun knightTrajectory() { 164 | assertEquals(listOf(square("d3")), knightTrajectory(square("d3"), square("d3"))) 165 | assertEquals(listOf(square("e4"), square("d6")), knightTrajectory(square("e4"), square("d6"))) 166 | knightTrajectory(square("f5"), square("g6")).assertKnightTrajectory(square("f5"), square("g6"), 2) 167 | knightTrajectory(square("g6"), square("g3")).assertKnightTrajectory(square("g6"), square("g3"), 3) 168 | knightTrajectory(square("d4"), square("a8")).assertKnightTrajectory(square("d4"), square("a8"), 3) 169 | knightTrajectory(square("h7"), square("f5")).assertKnightTrajectory(square("h7"), square("f5"), 4) 170 | knightTrajectory(square("g7"), square("h8")).assertKnightTrajectory(square("g7"), square("h8"), 4) 171 | knightTrajectory(square("a1"), square("a8")).assertKnightTrajectory(square("a1"), square("a8"), 5) 172 | knightTrajectory(square("a8"), square("h1")).assertKnightTrajectory(square("a8"), square("h1"), 6) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/lesson6/task2/Chess.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson6.task2 3 | 4 | import java.util.* 5 | 6 | /** 7 | * Клетка шахматной доски. Шахматная доска квадратная и имеет 8 х 8 клеток. 8 | * Поэтому, обе координаты клетки (горизонталь row, вертикаль column) могут находиться в пределах от 1 до 8. 9 | * Горизонтали нумеруются снизу вверх, вертикали слева направо. 10 | */ 11 | data class Square(val column: Int, val row: Int) { 12 | /** 13 | * Пример 14 | * 15 | * Возвращает true, если клетка находится в пределах доски 16 | */ 17 | fun inside(): Boolean = column in 1..8 && row in 1..8 18 | 19 | /** 20 | * Простая 21 | * 22 | * Возвращает строковую нотацию для клетки. 23 | * В нотации, колонки обозначаются латинскими буквами от a до h, а ряды -- цифрами от 1 до 8. 24 | * Для клетки не в пределах доски вернуть пустую строку 25 | */ 26 | fun notation(): String = TODO() 27 | } 28 | 29 | /** 30 | * Простая 31 | * 32 | * Создаёт клетку по строковой нотации. 33 | * В нотации, колонки обозначаются латинскими буквами от a до h, а ряды -- цифрами от 1 до 8. 34 | * Если нотация некорректна, бросить IllegalArgumentException 35 | */ 36 | fun square(notation: String): Square = TODO() 37 | 38 | /** 39 | * Простая 40 | * 41 | * Определить число ходов, за которое шахматная ладья пройдёт из клетки start в клетку end. 42 | * Шахматная ладья может за один ход переместиться на любую другую клетку 43 | * по вертикали или горизонтали. 44 | * Ниже точками выделены возможные ходы ладьи, а крестиками -- невозможные: 45 | * 46 | * xx.xxххх 47 | * xх.хxххх 48 | * ..Л..... 49 | * xх.хxххх 50 | * xx.xxххх 51 | * xx.xxххх 52 | * xx.xxххх 53 | * xx.xxххх 54 | * 55 | * Если клетки start и end совпадают, вернуть 0. 56 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 57 | * 58 | * Пример: rookMoveNumber(Square(3, 1), Square(6, 3)) = 2 59 | * Ладья может пройти через клетку (3, 3) или через клетку (6, 1) к клетке (6, 3). 60 | */ 61 | fun rookMoveNumber(start: Square, end: Square): Int = TODO() 62 | 63 | /** 64 | * Средняя 65 | * 66 | * Вернуть список из клеток, по которым шахматная ладья может быстрее всего попасть из клетки start в клетку end. 67 | * Описание ходов ладьи см. предыдущую задачу. 68 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start. 69 | * Между ними должны находиться промежуточные клетки, по порядку от start до end. 70 | * Примеры: rookTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 71 | * (здесь возможен ещё один вариант) 72 | * rookTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(3, 3), Square(6, 3)) 73 | * (здесь возможен единственный вариант) 74 | * rookTrajectory(Square(3, 5), Square(8, 5)) = listOf(Square(3, 5), Square(8, 5)) 75 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 76 | */ 77 | fun rookTrajectory(start: Square, end: Square): List = TODO() 78 | 79 | /** 80 | * Простая 81 | * 82 | * Определить число ходов, за которое шахматный слон пройдёт из клетки start в клетку end. 83 | * Шахматный слон может за один ход переместиться на любую другую клетку по диагонали. 84 | * Ниже точками выделены возможные ходы слона, а крестиками -- невозможные: 85 | * 86 | * .xxx.ххх 87 | * x.x.xххх 88 | * xxСxxxxx 89 | * x.x.xххх 90 | * .xxx.ххх 91 | * xxxxx.хх 92 | * xxxxxх.х 93 | * xxxxxхх. 94 | * 95 | * Если клетки start и end совпадают, вернуть 0. 96 | * Если клетка end недостижима для слона, вернуть -1. 97 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 98 | * 99 | * Примеры: bishopMoveNumber(Square(3, 1), Square(6, 3)) = -1; bishopMoveNumber(Square(3, 1), Square(3, 7)) = 2. 100 | * Слон может пройти через клетку (6, 4) к клетке (3, 7). 101 | */ 102 | fun bishopMoveNumber(start: Square, end: Square): Int = TODO() 103 | 104 | /** 105 | * Сложная 106 | * 107 | * Вернуть список из клеток, по которым шахматный слон может быстрее всего попасть из клетки start в клетку end. 108 | * Описание ходов слона см. предыдущую задачу. 109 | * 110 | * Если клетка end недостижима для слона, вернуть пустой список. 111 | * 112 | * Если клетка достижима: 113 | * - список всегда включает в себя клетку start 114 | * - клетка end включается, если она не совпадает со start. 115 | * - между ними должны находиться промежуточные клетки, по порядку от start до end. 116 | * 117 | * Примеры: bishopTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 118 | * bishopTrajectory(Square(3, 1), Square(3, 7)) = listOf(Square(3, 1), Square(6, 4), Square(3, 7)) 119 | * bishopTrajectory(Square(1, 3), Square(6, 8)) = listOf(Square(1, 3), Square(6, 8)) 120 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 121 | */ 122 | fun bishopTrajectory(start: Square, end: Square): List = TODO() 123 | 124 | /** 125 | * Средняя 126 | * 127 | * Определить число ходов, за которое шахматный король пройдёт из клетки start в клетку end. 128 | * Шахматный король одним ходом может переместиться из клетки, в которой стоит, 129 | * на любую соседнюю по вертикали, горизонтали или диагонали. 130 | * Ниже точками выделены возможные ходы короля, а крестиками -- невозможные: 131 | * 132 | * xxxxx 133 | * x...x 134 | * x.K.x 135 | * x...x 136 | * xxxxx 137 | * 138 | * Если клетки start и end совпадают, вернуть 0. 139 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 140 | * 141 | * Пример: kingMoveNumber(Square(3, 1), Square(6, 3)) = 3. 142 | * Король может последовательно пройти через клетки (4, 2) и (5, 2) к клетке (6, 3). 143 | */ 144 | fun kingMoveNumber(start: Square, end: Square): Int = TODO() 145 | 146 | /** 147 | * Сложная 148 | * 149 | * Вернуть список из клеток, по которым шахматный король может быстрее всего попасть из клетки start в клетку end. 150 | * Описание ходов короля см. предыдущую задачу. 151 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start. 152 | * Между ними должны находиться промежуточные клетки, по порядку от start до end. 153 | * Примеры: kingTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 154 | * (здесь возможны другие варианты) 155 | * kingTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(4, 2), Square(5, 2), Square(6, 3)) 156 | * (здесь возможен единственный вариант) 157 | * kingTrajectory(Square(3, 5), Square(6, 2)) = listOf(Square(3, 5), Square(4, 4), Square(5, 3), Square(6, 2)) 158 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 159 | */ 160 | fun kingTrajectory(start: Square, end: Square): List = TODO() 161 | 162 | /** 163 | * Сложная 164 | * 165 | * Определить число ходов, за которое шахматный конь пройдёт из клетки start в клетку end. 166 | * Шахматный конь одним ходом вначале передвигается ровно на 2 клетки по горизонтали или вертикали, 167 | * а затем ещё на 1 клетку под прямым углом, образуя букву "Г". 168 | * Ниже точками выделены возможные ходы коня, а крестиками -- невозможные: 169 | * 170 | * .xxx.xxx 171 | * xxKxxxxx 172 | * .xxx.xxx 173 | * x.x.xxxx 174 | * xxxxxxxx 175 | * xxxxxxxx 176 | * xxxxxxxx 177 | * xxxxxxxx 178 | * 179 | * Если клетки start и end совпадают, вернуть 0. 180 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 181 | * 182 | * Пример: knightMoveNumber(Square(3, 1), Square(6, 3)) = 3. 183 | * Конь может последовательно пройти через клетки (5, 2) и (4, 4) к клетке (6, 3). 184 | */ 185 | fun knightMoveNumber(start: Square, end: Square): Int = TODO() 186 | 187 | /** 188 | * Очень сложная 189 | * 190 | * Вернуть список из клеток, по которым шахматный конь может быстрее всего попасть из клетки start в клетку end. 191 | * Описание ходов коня см. предыдущую задачу. 192 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start. 193 | * Между ними должны находиться промежуточные клетки, по порядку от start до end. 194 | * Примеры: 195 | * 196 | * knightTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 197 | * здесь возможны другие варианты) 198 | * knightTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(5, 2), Square(4, 4), Square(6, 3)) 199 | * (здесь возможен единственный вариант) 200 | * knightTrajectory(Square(3, 5), Square(5, 6)) = listOf(Square(3, 5), Square(5, 6)) 201 | * (здесь опять возможны другие варианты) 202 | * knightTrajectory(Square(7, 7), Square(8, 8)) = 203 | * listOf(Square(7, 7), Square(5, 8), Square(4, 6), Square(6, 7), Square(8, 8)) 204 | * 205 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 206 | */ 207 | fun knightTrajectory(start: Square, end: Square): List = TODO() 208 | -------------------------------------------------------------------------------- /tutorial/chapter05_5.adoc: -------------------------------------------------------------------------------- 1 | = 5.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/lesson7/task2/Matrices.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson7.task2 3 | 4 | import lesson7.task1.Matrix 5 | import lesson7.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..matrix.width - 1) { 24 | for (j in 0..matrix.height - 1) { 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..height - 1) { 43 | for (j in 0..width - 1) { 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 | -------------------------------------------------------------------------------- /tutorial/chapter08.adoc: -------------------------------------------------------------------------------- 1 | = 8. Файловые операции 2 | 3 | Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой -- 4 | операционной системой, устройствами компьютера, сетью Интернет. 5 | Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, 6 | но сейчас такой способ применяется крайне редко. 7 | Более совершенным способом взаимодействия являются __файлы__ -- 8 | многие программы берут из них настроечную или входную информаци, 9 | и используют их для сохранения результатов своей работы или различных настроек. 10 | Например, всевозможные __редакторы__ позволяют открывать файлы в определённом формате, 11 | просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет. 12 | 13 | В библиотеке Java внутри пакета `java.io` имеется ряд классов, обеспечивающих возможность работы с файлами, 14 | а библиотека Котлина дополняет эти классы некоторыми удобными возможностями. 15 | Как обычно, рассмотрим часть этих возможностей на примере. 16 | 17 | [source,kotlin] 18 | ---- 19 | import java.io.File 20 | /** 21 | * Пример 22 | * 23 | * Во входном файле с именем inputName содержится некоторый текст. 24 | * Вывести его в выходной файл с именем outputName, выровняв по левому краю, 25 | * чтобы длина каждой строки не превосходила lineLength. 26 | * Слова в слишком длинных строках следует переносить на следующую строку. 27 | * Слишком короткие строки следует дополнять словами из следующей строки. 28 | * Пустые строки во входном файле обозначают конец абзаца, 29 | * их следует сохранить и в выходном файле 30 | */ 31 | fun alignFile(inputName: String, lineLength: Int, outputName: String) { 32 | val writer = File(outputName).bufferedWriter() 33 | var currentLineLength = 0 34 | for (line in File(inputName).readLines()) { 35 | if (line.isEmpty()) { 36 | writer.newLine() 37 | if (currentLineLength > 0) { 38 | writer.newLine() 39 | currentLineLength = 0 40 | } 41 | } 42 | for (word in line.split(Regex("\\s+"))) { 43 | if (currentLineLength > 0) { 44 | if (word.length + currentLineLength >= lineLength) { 45 | writer.newLine() 46 | currentLineLength = 0 47 | } 48 | else { 49 | writer.write(" ") 50 | currentLineLength++ 51 | } 52 | } 53 | writer.write(word) 54 | currentLineLength += word.length 55 | } 56 | } 57 | writer.close() 58 | } 59 | ---- 60 | 61 | Краеугольный тип, используемый для работы с файлами в Котлине -- тип `java.io.File`. 62 | В соответствии с названием, он предназначен для различных операций с файлами; 63 | объект этого типа соответствует какому-либо реальному файлу, чаще всего находящемуся на жёстком диске. 64 | Для создания объекта используется конструктор: `File(inputName)` или `File(outputName)` в примере. 65 | Если в аргументе конструктора указано только имя файла -- поиск файла происходит в текущей директории, 66 | а если аргумент содержим также путь к файлу -- то в директории, указанной этим путём. 67 | 68 | Обмен данными с файлом может происходить в режиме чтения либо в режиме записи. 69 | В режиме чтения информации, заданное имя должно соответствовать уже существующему файлу. 70 | Один из способов получения информации из файла -- вызов функции `file.readLines()`. 71 | Результат вызова -- список строк, из которых состоит файл. 72 | Каждый `String` в этом списке соответствует одной строке файла, 73 | строки файла разделяются символом "возврат каретки" и / или "новая строка". 74 | 75 | В режиме записи информации, заданное имя может не соответствовать существующему файлу -- в этом случае он будет создан. 76 | Для записи информации, необходимо создать один из объектов, обеспечивающих такую возможность. 77 | В примере, таким объектом является `val writer = File(outputName).bufferedWriter()` -- 78 | то есть необходимо вызвать функцию `bufferedWriter()` на получателе, соответствующем исходному файлу. 79 | Как видно из текста примера, `writer` (писатель) имеет функции `writer.newLine()` (добавление в файл новой строки), 80 | `writer.write(string)` (добавление в файл заданной строки) и `writer.close()` (закрытие писателя, 81 | выполняется строго ПОСЛЕ выполнения всех остальных действий и фиксирует итоговое состояние файла). 82 | 83 | Мы перечислили все файловые операции, присутствующие в исходном примере. 84 | Внутри цикла `for`, каждая из строк файла разбивается по пробелам на слова, с этой целью используется `Regex("\\s+")`. 85 | В `currentLineLength` накапливается длина текущей строки ВЫХОДНОГО файла. 86 | Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, 87 | в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. 88 | Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений. 89 | 90 | == За занавесом: чтение из файла 91 | 92 | Пакет `java.io` позволяет работать с файлами на трёх разных уровнях: 93 | 94 | 1. Уровень отдельных байт. В этом случае файл воспринимается как массив или, точнее, как поток байт. Поток, в отличие от массива, можно только перебирать, с сильно ограниченными возможностями по возвращению назад. Для этой цели имеется тип `java.io.InputStream`. 95 | 1. Уровень символов. В этом случае файл воспринимается уже как поток символов типа `Char`, то есть каждые несколько байт файла превращаются в определённый символ -- с учётом заданной кодировки файла. Для этой цели имеется тип `java.io.InputStreamReader`, который внутри себя использует `InputStream` для чтения байт. 96 | 1. Уровень строк. На этом уровне файл воспринимается как набор строк `String`, составленных из символов по определённым правилам -- чаще всего используется разделение по отдельным строкам файла. Эту роль выполняет тип `java.io.BufferedReader`, использующий внутри себя `InputStreamReader` для чтения символов. 97 | 98 | При программировании на Java каждый из этих объектов приходится создавать отдельно -- 99 | вначале `InputStream`, потом `InputStreamReader` и, наконец, `BufferedReader`. 100 | Библиотека Котлина позволяет создать любой из этих объектов сразу, используя файл-получатель: 101 | 102 | 1. `file.inputStream()` создаёт байтовый поток. 103 | 1. `file.reader()` создаёт читатель символов, используя кодировку по умолчанию. `file.reader(Charset.forName("CP1251"))` создаёт писатель с заданной кодировкой (в данном случае CP1251). 104 | 1. Наконец, `file.bufferedReader()` создаёт буферизованный читатель строк. Опять-таки, может быть задана нужная кодировка, иначе используется кодировка по умолчанию. 105 | 106 | Набор функций у данных трёх объектов различается. 107 | У всех у них есть функция `close()`, закрывающая исходный файл в конце работы с потоком. 108 | Также, у них имеется функция высшего порядка `use { ... }`, 109 | выполняющая описанные в лямбде действия и закрывающая файл в конце своей работы автоматически. 110 | Скажем, исходный пример можно было бы переписать с помощью `use` так: 111 | 112 | [source,kotlin] 113 | ---- 114 | fun alignFile(inputName: String, lineLength: Int, outputName: String) { 115 | File(outputName).bufferedWriter().use { 116 | var currentLineLength = 0 117 | for (line in File(inputName).readLines()) { 118 | if (line.isEmpty()) { 119 | it.newLine() 120 | if (currentLineLength > 0) { 121 | it.newLine() 122 | currentLineLength = 0 123 | } 124 | continue 125 | } 126 | for (word in line.split(" ")) { 127 | if (currentLineLength > 0) { 128 | if (word.length + currentLineLength >= lineLength) { 129 | it.newLine() 130 | currentLineLength = 0 131 | } else { 132 | it.write(" ") 133 | currentLineLength++ 134 | } 135 | } 136 | it.write(word) 137 | currentLineLength += word.length 138 | } 139 | } 140 | } 141 | } 142 | ---- 143 | 144 | Здесь исходный `BufferedWriter` в лямбде становится параметром `it`. 145 | Заметим, что при использовании `use` исходный файл будет закрыт как при корректном завершении функции, 146 | так и при возникновении исключения. 147 | 148 | Кроме этого, каждый объект обладает своими методами для чтения информации: 149 | 150 | 1. `inputStream.read()` читает из `InputStream` очередной байт, возвращая его в виде результата типа `Int`. Если файл закончен, результат этой функции будет -1. `inputStream.read(byteArray)` читает сразу несколько байт, записывая их в массив байт (число прочитанных байт равно размеру массива). `inputStream.read(byteArray, offset, length)` записывает в `byteArray` `length` байт, начиная с индекса `offset`. 151 | 1. `reader.read()` читает из `InputStreamReader` очередной символ, возвращая его в виде результата типа `Int`. Здесь используется именно `Int`, а не `Char`, так как, во-первых, символ в общем случае может не поместиться в двухбайтовые тип и, во-вторых, чтобы вернуть -1 в случае неудачи. Есть аналогичные методы для чтения символьного массива (НЕ строки) с возможным указанием смещения и числа символов -- см. выше про байтовый массив. 152 | 1. `bufferedReader.readLine()` читает из `BufferedReader` очередную строку (до перевода строки). `bufferedReader.readLines()` читает сразу же все строки. Есть ряд других методов для работы со строками по отдельности. 153 | 154 | Следует отметить, что все функции чтения информации могут бросить исключение `IOException` в том случае, 155 | если чтение по какой-либо причине невозможно (например, если файл не существует или недоступен). 156 | 157 | В примере, мы вообще не создавали `bufferedReader`, а использовали функцию `file.readLines()`. 158 | Она создаёт `bufferedReader` внутри себя и обращается к его функции `readLines()`. 159 | После чтения последней строки файл закрывается. 160 | 161 | == За занавесом: запись в файл 162 | 163 | Запись в файл использует те же три уровня: байты `OutputStream`, символы `OutputStreamWriter` и строки `BufferedWriter`. 164 | Для записи байт либо символов используются функции `write`, 165 | аргументом которых может являться целое число (в котором хранится байт или код символа) или массив (опять-таки байт или символов). 166 | Эти функции не имеют результата и бросают `IOException`, если файл недоступен для записи. 167 | `BufferedWriter` может использовать функцию `write` также для записи строк. 168 | Как и все три вида потоков чтения, 169 | потоки записи необходимо закрывать после использования с помощью `close()` или `use { ... }`. 170 | 171 | Сверх этого, для записи часто используется так называемый поток печати `PrintStream`. 172 | В Котлине его можно создать из файла, используя функцию `file.printStream()`. 173 | Поток печати расширяет обычный байтовый поток рядом дополнительных возможностей: 174 | 175 | 1. `printStream.println(...)` -- вывод заданной строки или строкового представления с последующим переходом на новую строку. 176 | 1. `printStream.print(...)` -- то же, но без перехода на новую строку. 177 | 1. `printStream.format(formatString, ...)` -- форматированный вывод (происходит по принципу, описанном в разделе 5). 178 | 179 | == Упражнения 180 | 181 | Откройте файл `srс/lesson8/task1/Files.kt` в проекте `KotlinAsFirst`. 182 | Он содержит ряд задач, каждая из которых предполагает наличие входного и/или выходного файла. 183 | Решите хотя бы одну-две из имеющихся задач, используя описанные в этом разделе приёмы. 184 | Обратите внимание на задачи, помеченные как "Сложная" или "Очень сложная", попробуйте решить одну из них. 185 | 186 | Протестируйте свою реализацию, используя тесты из `test/lesson8/task1/Tests.kt`. 187 | Обратите внимание, что тесты используют готовые входные файлы, расположенные в директории `input` нашего проекта. 188 | Убедитесь, что тесты успешно проходят, обязательно создайте два-три дополнительных теста. 189 | Постарайтесь внутри этих тестов проверить какие-либо необычные ситуации, 190 | которые могут возникнуть в выбранной вами задаче. 191 | 192 | Поздравляем! Выполнив упражнения по этому разделу, вы успешно завершили наш курс. 193 | -------------------------------------------------------------------------------- /tutorial/chapter00.adoc: -------------------------------------------------------------------------------- 1 | = Введение 2 | 3 | Языки программирования -- интереснейшая область современной техники. 4 | За последние 30-40 лет информационные технологии разрослись до невероятных пределов, 5 | и сейчас мало кто в состоянии обозреть эту область в полном объёме. 6 | Компьютерные программы выросли с нескольких сотен строк до десятков миллионов строк, 7 | применяются сейчас в самых разных областях и запускаются на самых разных __платформах__, например: 8 | 9 | * обыкновенные программы для персонального компьютера, часто называемые desktop-программами; 10 | * web-программы, которые делятся, в свою очередь, на __клиентскую__ часть, выполняющуюся на компьютере пользователя, и __серверную__, выполняющуюся на сервере; 11 | * __мобильные__ программы для планшетов, смартфонов и других мобильных устройств; 12 | * __системные__ программы, являющиеся частью операционной системы; 13 | * __встраиваемые__ программы, являющиеся частью встраиваемых систем управления (применяемые, например, в транспорте, банкоматах, станках с программным управлением, при программировании роботов). 14 | 15 | Для написания разных видов программ сейчас применяются разные языки программирования. 16 | Например, в сфере __мобильных__ программ сейчас правят бал языки Swift (мобильные устройства под управлением iOS) 17 | и Java (устройства под управлением Android). 18 | __Системные__ программы, как правило, пишутся на языках C или {cpp}. 19 | Эти же языки долгое время использовались и для создания __встраиваемых__ программ, 20 | но в последние годы в этой области набирает популярность язык Java. 21 | Для написания web-клиентов часто используется JavaScript, а в простых случаях -- язык разметки страниц HTML. 22 | Web-серверы используют опять-таки Java (в сложных случаях), а также Python и PHP (в более простых). 23 | Наконец, простые desktop-программы сейчас могут быть написаны на самых разных языках, 24 | и выбор во многом зависит от сложности программы, области её использования, предполагаемой операционной системы. 25 | В первую очередь следует назвать языки Java, {cpp}, C#, Python, Visual Basic, Ruby, Swift. 26 | 27 | Самым универсальным и одновременно самым распространённым языком программирования 28 | на данный момент следует считать язык Java. 29 | Java в широком смысле -- не только язык, но и платформа для выполнения программ 30 | под самыми разными операционными системами и на разной аппаратуре. 31 | Такая универсальность обеспечивается наличием виртуальной машины Java -- 32 | системной программы, интерпретирующей Java байт-код в машинные коды конкретного компьютера или системы. 33 | Java также включает богатейший набор библиотек для разработки. 34 | 35 | Однако, для начинающих язык Java является несколько многословным и сложным. 36 | Это пособие посвящено другому языку программирования, спутнику Java -- языку Котлин. 37 | Котлин -- молодой, лёгкий для изучения язык программирования, позволяющий писать программы под платформы JVM и Android 38 | более лаконично, просто и с меньшим количеством ошибок по сравнению с языком Java. 39 | Котлин и Java -- полностью __интероперабельные__ языки, 40 | поэтому одна и та же программа может быть частично написана на Котлине, частично на Java. 41 | Программы на Котлине могут использовать все имеющиеся Java-библиотеки, и наоборот. 42 | На данный момент программы на Котлине пишут десятки тысяч программистов, 43 | основная ниша его промышленного применения -- мобильные программы под платформу Android 44 | и в несколько меньшей степени -- web-разработка. 45 | 46 | В ходе изучения Котлина мы изучим также многие элементы стандартной библиотеки Java, 47 | а понимание работы программ на Котлине во многом упростит понимание работы Java-программ. 48 | 49 | == Что требуется для начала 50 | 51 | Самый простой способ начать программировать на Котлине -- зайти на сайт http://try.kotlinlang.org. 52 | Имеющаяся там "песочница" позволяет писать программы прямо в браузере, 53 | с возможностью выполнять и сохранять свои программы и проходить обучающие курсы. 54 | 55 | Масштабы песочницы, однако, достаточны только для небольших программ, 56 | а более-менее серьёзные программы, как правило, разрабатываются в интегрированной среде (IDE). 57 | Разработка под платформу Java в любом случае требует установки пакета JDK, 58 | который необходимо скачать с сайта компании Oracle: http://www.oracle.com/technetwork/java/javase/downloads/index.html. 59 | Первое время вам потребуется Java Platform, Standard Edition, 60 | на август 2016 года последняя её версия -- Java SE 8.102. 61 | 62 | В качестве интегрированной среды разработки рекомендую установить Intellij IDEA Community Edition, 63 | её следует брать отсюда: https://www.jetbrains.com/idea/#chooseYourEdition. 64 | Community Edition является полностью бесплатной, 65 | базовая версия обеспечивает поддержку программирования на Java, Kotlin, Scala, Groovy, 66 | поддержку систем контроля версий Git, Mercurial, SVN, интеграцию с системами сборки Maven и Gradle. 67 | 68 | Для интеграции IDEA с системой контроля версий Git необходимо установить один из клиентов Git. 69 | Таких клиентов существует много; "родной" Git клиент можно скачать отсюда: https://git-scm.com/downloads. 70 | Имейте в виду, что в IDEA интегрирован собственный Git-плагин, уже имеющий графический интерфейс, 71 | поэтому скачивать и устанавливать клиенты Git с графическим интерфейсом (GUI Clients) необязательно. 72 | 73 | == Учебный проект 74 | 75 | В ходе обучения мы будем активно использовать проект "Котлин как первый язык программирования", содержащий текст данного пособия и около сотни различных задач на языке Kotlin. 76 | Оригинальный код данного проекта доступен по адресу https://github.com/Kotlin-Polytech/KotlinAsFirst2016 на сайте GitHub, который является специализированным хранилищем программных кодов и основан на системе контроля версий Git. 77 | Для того, чтобы начать работать с этим проектом, Вам необходимо выполнить следующие действия. 78 | 79 | 1. Зарегистрироваться на https://github.com/ (в случае, если у Вас еще нет GitHub аккаунта). Далее выбранное Вами имя будет обозначаться как . 80 | 1. Создать специальную копию репозитория проекта -- _форк_. Для этого достаточно зайти на страницу проекта https://github.com/Kotlin-Polytech/KotlinAsFirst2016 и нажать кнопку `Fork` в правом верхнем углу страницы. После этого Ваша персональная копия проекта станет доступна по адресу https://github.com//KotlinAsFirst2016, и всю работу по решению различных задач Вы должны выполнять именно с Вашей копией. 81 | 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//KotlinAsFirst2016 и место на компьютере, куда будет скачан проект (Parent Directory). 82 | 1. При открытии проекта в правом нижнем углу среды разработки Вы увидите сообщение "Non-managed pom.xml file found" с предложениями `Add as Maven project` и `Disable notification`. Нажмите на ссылку `Add as Maven project`, это необходимо для загрузки на Ваш компьютер всех библиотек, необходимых для работы обучающего проекта. 83 | 84 | Проект содержит задачи, разбитые на восемь уроков (lesson). 85 | Тексты задач доступны через окно Project в Intellij IDEA (открывается комбинацией клавиш Alt+1). 86 | В папках src/lessonX, где X -- номер урока, находятся примеры решённых задач к данному уроку, 87 | тексты задач, которые необходимо решить и готовые заглушки функций для написания решения. 88 | В папках test/lessonX находятся тестовые функции к задачам. Подробнее о задачах и тестах см. раздел 1 этого пособия. 89 | 90 | При открытии в первый раз любого файла с расширением kt Вы увидите сообщение над ним `Project SDK is not defined` и ссылку `Setup SDK` справа. Нажмите на эту ссылку и выберете JDK 1.8 для работы с проектом в появившемся окне. Если список JDK в окне пуст, или не содержит JDK 1.8, следует нажать на клавишу `Configure`, затем зелёный плюс в верхнем левом углу и зарегистрировать установленную на Вашем компьютере JDK 1.8 в Intellij IDEA. Если Вы забыли установить JDK, это следует сделать, предварительно скачав её с сайта Oracle. 91 | 92 | Работа с проектом будет осуществляться в рамках системы контроля версий Git, 93 | а Ваши решения будут представлять собой коммиты в соответствующий репозиторий. 94 | Коммит -- это законченное изменение текста проекта (например, решение одной или нескольких задач из какого-либо урока). 95 | Для более глубокого понимания того, что такое системы контроля версий, зачем они нужны и как с ними работать, 96 | рекомендуется пройти один или несколько обучающих уроков из Интернета. 97 | Несколько примеров подобных туториалов приведены ниже: 98 | 99 | * https://githowto.com/ru/ 100 | * https://try.github.io/levels/1/challenges/1 101 | * https://guides.github.com/activities/hello-world/ 102 | 103 | В ходе освоения различных уроков вы можете обращаться за помощью к преподавателю или своим однокурсникам, 104 | но важно, чтобы ХОТЯ БЫ ОДНА задача из каждого урока была решена вами самостоятельно. 105 | Имейте в виду, что задачи разбиты на пять уровней сложности, сложность задачи указана в комментарии к ней 106 | (тривиальная Trivial, простая Easy, средняя Normal, сложная Hard и очень сложная Impossible). 107 | Решайте в первую очередь те задачи, которые кажутся вам по силам, но иногда пытайтесь решать и более сложные. 108 | Если у вас нет идей, как решить какую-либо сложную задачу -- попросите преподавателя о подсказке. 109 | Оценка по итогам курса будет зависеть от сложности решённых вами задач из различных уроков. 110 | 111 | Законченное решение задачи (или нескольких задач из одного урока) следует 112 | фиксировать в виде коммита в системе контроля версий. Для этого необходимо: 113 | 114 | 1. Зайти в окно Version Control среды Intellij IDEA. Это делается из меню `View` -> `Tool Windows` либо с помощью комбинации клавиш `Alt+9`. 115 | 1. В нём щелчком мыши выбрать вкладку `Local Changes` (локальные изменения). Убедитесь, что файлы, которые вы меняли, присутствуют в этом окне. 116 | 1. В контекстном меню выберете команду `Commit Changes`. В появившемся окне введите осмысленный комментарий к вашему изменению (например, "Решена задача такая-то"), откройте выпадающее меню справа от кнопки `Commit` и в нём выберете команду `Commit and Push`. 117 | 1. При появлении соответствующих окон введите своё имя и e-mail для идентификации автора коммита (эти поля заполняются один раз), а также логин и пароль для Вашего аккаунта на GitHub. 118 | 119 | После этого Вы должны отправить эти задачи на проверку в основной репозиторий 120 | в виде _запроса на обновление_ (pull request). Для этого необходимо сделать следующее. 121 | 122 | 1. Зайти на https://github.com//KotlinAsFirst2016/pulls и нажать кнопку `New pull request`. 123 | 1. Проверить, что следующие поля содержат правильные значения: 124 | * `base fork` -> `Kotlin-Polytech/KotlinAsFirst2016` 125 | * `base` -> `master` 126 | * `head fork` -> `/KotlinAsFirst2016` 127 | * `compare` -> `master` 128 | 1. Проверить, что Ваши изменения могут быть применены к основному репозиторию -- об этом свидетельствует зеленая надпись **Able to merge** (и в нормальной ситуации Вы её должны увидеть). Если данная надпись не появляется и вместо неё вы видите надпись **Can't automatically merge**, всё равно создайте запрос (см. следующий пункт), но после этого отправьте сообщение на электронную почту своему преподавателю. 129 | 1. Нажать кнопку `Create pull request` и заполнить его имя и краткое описание. Желательно использовать говорящие имена и адекватные описания содержания выполненной Вами работы. 130 | 131 | Созданный запрос будет автоматически проверен (это возможно, только если вы видели надпись **Able to merge** при создании запроса), после чего на его странице появится краткий отчет, в котором будет указано: 132 | 133 | * Задачи из каких уроков решались в данном пулл реквесте 134 | * Сколько задач каждого типа было успешно решено 135 | * Список успешно решенных задач 136 | * Список задач, решенных не полностью или неправильно, с перечислением некоторых из неудавшихся тестов. 137 | 138 | Преподаватели могут написать в Pull Request дополнительный комментарий, описывающий сделанные вами ошибки. 139 | Если же все решённые вами задачи успешно прошли тестирование, Pull Request будет закрыт -- 140 | это свидетельствует о том, что ваши решения приняты в полном объёме. 141 | 142 | В том случае, если все задачи, которые Вы хотели решить, успешно проверены, Вы можете приступать к следующим задачам. 143 | Если часть задач решена неправильно, постарайтесь исправить возможные ошибки при помощи предоставленных Вам тестов. 144 | В случае, если Вы уверены в правильности решения или не можете понять, где Вы ошиблись при решении, можете обратиться к преподавателю. 145 | -------------------------------------------------------------------------------- /test/lesson7/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson7.task2 2 | 3 | import lesson7.task1.Matrix 4 | import lesson7.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..height - 1) { 13 | for (column in 0..width - 1) { 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 = lesson7.task1.createMatrix(height, width, this[0, 0]) 237 | for (row in 0..height - 1) { 238 | for (column in 0..width - 1) { 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 | } -------------------------------------------------------------------------------- /test/lesson8/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson8.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 java.io.File 7 | 8 | class Tests { 9 | 10 | private fun assertFileContent(name: String, expectedContent: String) { 11 | val file = File(name) 12 | val content = file.readLines().joinToString("\n") 13 | assertEquals(expectedContent, content) 14 | } 15 | 16 | @Test 17 | @Tag("Example") 18 | fun alignFile() { 19 | alignFile("input/align_in1.txt", 50, "temp.txt") 20 | assertFileContent("temp.txt", 21 | """Для написания разных видов программ сейчас 22 | применяются разные языки программирования. 23 | Например, в сфере мобильных программ сейчас правят 24 | бал языки Swift (мобильные устройства под 25 | управлением iOS) и Java (устройства под 26 | управлением Android). Системные программы, как 27 | правило, пишутся на языках C или {cpp}. Эти же 28 | языки долгое время использовались и для создания 29 | встраиваемых программ, но в последние годы в этой 30 | области набирает популярность язык Java. Для 31 | написания web-клиентов часто используется 32 | JavaScript, а в простых случаях -- язык разметки 33 | страниц HTML. Web-серверы используют опять-таки 34 | Java (в сложных случаях), а также Python и PHP (в 35 | более простых). Наконец, простые desktop-программы 36 | сейчас могут быть написаны на самых разных языках, 37 | и выбор во многом зависит от сложности программы, 38 | области её использования, предполагаемой 39 | операционной системы. В первую очередь следует 40 | назвать языки Java, {cpp}, C#, Python, Visual 41 | Basic, Ruby, Swift. 42 | 43 | Самым универсальным и одновременно самым 44 | распространённым языком программирования на данный 45 | момент следует считать язык Java. Java в широком 46 | смысле -- не только язык, но и платформа для 47 | выполнения программ под самыми разными 48 | операционными системами и на разной аппаратуре. 49 | Такая универсальность обеспечивается наличием 50 | виртуальной машины Java -- системной программы, 51 | интерпретирующей Java байт-код в машинные коды 52 | конкретного компьютера или системы. Java также 53 | включает богатейший набор библиотек для 54 | разработки.""") 55 | File("temp.txt").delete() 56 | } 57 | 58 | @Test 59 | @Tag("Normal") 60 | fun countSubstrings() { 61 | assertEquals(mapOf("РАЗНЫЕ" to 2, "ные" to 2, "Неряшливость" to 1, "е" to 49, "эволюция" to 0), 62 | countSubstrings("input/substrings_in1.txt", listOf("РАЗНЫЕ", "ные", "Неряшливость", "е", "эволюция"))) 63 | } 64 | 65 | @Test 66 | @Tag("Normal") 67 | fun sibilants() { 68 | sibilants("input/sibilants_in1.txt", "temp.txt") 69 | assertFileContent("temp.txt", 70 | """/** 71 | * Простая 72 | * 73 | * В русском языке, как правило, после букв Ж, Ч, Ш, Щ пишется И, А, У, а не Ы, Я, Ю. 74 | * Во входном файле с именем inputName содержится некоторый текст. 75 | * Проверить текст во входном файле на соблюдение данного правила и вывести в выходной 76 | * файл outputName текст с исправленными ошибками. 77 | * 78 | * Регистр заменённых букв следует сохранять. 79 | * 80 | * Исключения (жУри, броШУра, параШут) в рамках данного задания обрабатывать не нужно 81 | * 82 | * жИ шИ ЖИ Ши ЖА шА Жа ша жу шу жу щу ча шу щу ща жа жи жи жу чу ча 83 | */""") 84 | File("temp.txt").delete() 85 | } 86 | 87 | @Test 88 | @Tag("Normal") 89 | fun centerFile() { 90 | centerFile("input/center_in1.txt", "temp.txt") 91 | assertFileContent("temp.txt", 92 | """ Съешь же ещё этих мягких французских булок, да выпей чаю. 93 | Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства. 94 | Тест 95 | """ + // Avoiding trailing whitespaces problem 96 | """ 97 | Hello World 98 | Во входном файле с именем inputName содержится некоторый текст. 99 | Вывести его в выходной файл с именем outputName, выровняв по центру.""") 100 | File("temp.txt").delete() 101 | 102 | } 103 | 104 | @Test 105 | @Tag("Hard") 106 | fun alignFileByWidth() { 107 | alignFileByWidth("input/width_in1.txt", "temp.txt") 108 | assertFileContent("temp.txt", 109 | """Простая 110 | 111 | Во входном файле с именем inputName содержится некоторый текст. 112 | Вывести его в выходной файл с именем outputName, выровняв по левому и правому краю относительно 113 | самой длинной строки. 114 | Выравнивание производить, вставляя дополнительные пробелы между словами: равномерно по всей строке 115 | 116 | Слова внутри строки отделяются друг от друга одним или более пробелом. 117 | 118 | Следующие правила должны быть выполнены: 119 | 1) Каждая строка входного и выходного файла не должна заканчиваться пробелом. 120 | 2) Пустые строки или строки из пробелов во входном файле должны превратиться в пустые строки в выходном файле. 121 | 3) Число строк в выходном файле должно быть равно числу строк во входном (в т. ч. пустых). 122 | 123 | Равномерность определяется следующими формальными правилами: 124 | 1) Число пробелов между каждыми двумя парами соседних слов не должно отличаться более, чем на 1. 125 | 2) Число пробелов между более левой парой соседних слов должно быть больше или равно числу пробелов 126 | между более правой парой соседних слов.""") 127 | File("temp.txt").delete() 128 | 129 | } 130 | 131 | @Test 132 | @Tag("Normal") 133 | fun top20Words() { 134 | assertEquals(mapOf( 135 | "и" to 1106, 136 | "в" to 674, 137 | "не" to 411, 138 | "он" to 306, 139 | "на" to 290, 140 | "я" to 261, 141 | "с" to 260, 142 | "как" to 211, 143 | "но" to 210, 144 | "что" to 187, 145 | "все" to 131, 146 | "к" to 130, 147 | "она" to 126, 148 | "его" to 109, 149 | "за" to 105, 150 | "то" to 104, 151 | "а" to 98, 152 | "ее" to 95, 153 | "мне" to 95, 154 | "уж" to 95 155 | ), lesson8.task1.top20Words("input/onegin.txt")) 156 | } 157 | 158 | @Test 159 | @Tag("Normal") 160 | fun transliterate() { 161 | transliterate( 162 | "input/trans_in1.txt", 163 | mapOf('з' to "zz", 'р' to "r", 'д' to "d", 'й' to "y", 'М' to "m", 'и' to "yy", '!' to "!!!"), 164 | "temp.txt" 165 | ) 166 | assertFileContent("temp.txt", "Zzdrавствуy,\nmyyr!!!") 167 | File("temp.txt").delete() 168 | } 169 | 170 | @Test 171 | @Tag("Normal") 172 | fun chooseLongestChaoticWord() { 173 | chooseLongestChaoticWord("input/chaotic_in1.txt", "temp.txt") 174 | assertFileContent("temp.txt", "Карминовый, Некрасивый") 175 | File("temp.txt").delete() 176 | } 177 | 178 | 179 | private fun checkHtmlSimpleExample() { 180 | val result = File("temp.html").readText().replace(Regex("[\\s\\n\\t]"), "") 181 | val expected = 182 | """ 183 | 184 | 185 |

186 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 187 | Vestibulum lobortis. Est vehicula rutrum suscipit, ipsum libero placerat tortor. 188 |

189 |

190 | Suspendisse et elit in enim tempus iaculis. 191 |

192 | 193 | 194 | """.trimIndent().replace(Regex("[\\s\\n\\t]"), "") 195 | assertEquals(expected, result) 196 | 197 | File("temp.html").delete() 198 | } 199 | 200 | @Test 201 | @Tag("Hard") 202 | fun markdownToHtmlSimple() { 203 | markdownToHtmlSimple("input/markdown_simple.md", "temp.html") 204 | checkHtmlSimpleExample() 205 | } 206 | 207 | private fun checkHtmlListsExample() { 208 | val result = File("temp.html").readText().replace(Regex("[\\s\\n\\t]"), "") 209 | val expected = 210 | """ 211 | 212 | 213 |
    214 |
  • 215 | Утка по-пекински 216 |
      217 |
    • Утка
    • 218 |
    • Соус
    • 219 |
    220 |
  • 221 |
  • 222 | Салат Оливье 223 |
      224 |
    1. Мясо 225 |
        226 |
      • 227 | Или колбаса 228 |
      • 229 |
      230 |
    2. 231 |
    3. Майонез
    4. 232 |
    5. Картофель
    6. 233 |
    7. Что-то там ещё
    8. 234 |
    235 |
  • 236 |
  • Помидоры
  • 237 |
  • 238 | Фрукты 239 |
      240 |
    1. Бананы
    2. 241 |
    3. 242 | Яблоки 243 |
        244 |
      1. Красные
      2. 245 |
      3. Зелёные
      4. 246 |
      247 |
    4. 248 |
    249 |
  • 250 |
251 | 252 | 253 | """.trimIndent().replace(Regex("[\\s\\n\\t]"), "") 254 | assertEquals(expected, result) 255 | 256 | File("temp.html").delete() 257 | } 258 | 259 | @Test 260 | @Tag("Hard") 261 | fun markdownToHtmlLists() { 262 | markdownToHtmlLists("input/markdown_lists.md", "temp.html") 263 | checkHtmlListsExample() 264 | } 265 | 266 | @Test 267 | @Tag("Impossible") 268 | fun markdownToHtml() { 269 | markdownToHtml("input/markdown_simple.md", "temp.html") 270 | checkHtmlSimpleExample() 271 | 272 | markdownToHtml("input/markdown_lists.md", "temp.html") 273 | checkHtmlListsExample() 274 | } 275 | 276 | @Test 277 | @Tag("Normal") 278 | fun printMultiplicationProcess() { 279 | fun test(lhv: Int, rhv: Int, res: String) { 280 | printMultiplicationProcess(lhv, rhv, "temp.txt") 281 | assertFileContent("temp.txt", res.trimIndent()) 282 | File("temp.txt").delete() 283 | } 284 | 285 | test(19935, 286 | 111, 287 | """ 288 | 19935 289 | * 111 290 | -------- 291 | 19935 292 | + 19935 293 | +19935 294 | -------- 295 | 2212785 296 | """ 297 | ) 298 | 299 | test(12345, 300 | 76, 301 | """ 302 | 12345 303 | * 76 304 | ------- 305 | 74070 306 | +86415 307 | ------- 308 | 938220 309 | """ 310 | ) 311 | 312 | test(12345, 313 | 6, 314 | """ 315 | 12345 316 | * 6 317 | ------ 318 | 74070 319 | ------ 320 | 74070 321 | """ 322 | ) 323 | 324 | } 325 | 326 | @Test 327 | @Tag("Impossible") 328 | fun printDivisionProcess() { 329 | 330 | fun test(lhv: Int, rhv: Int, res: String) { 331 | printDivisionProcess(lhv, rhv, "temp.txt") 332 | assertFileContent("temp.txt", res.trimIndent()) 333 | File("temp.txt").delete() 334 | } 335 | 336 | test(199735, 337 | 22, 338 | """ 339 | 19935 | 22 340 | -198 906 341 | ---- 342 | 13 343 | -0 344 | -- 345 | 135 346 | -132 347 | ---- 348 | 3 349 | """ 350 | ) 351 | 352 | test(2, 353 | 20, 354 | """ 355 | 2 | 20 356 | -0 0 357 | -- 358 | 2 359 | """ 360 | ) 361 | 362 | test(99999, 363 | 1, 364 | """ 365 | 99999 | 1 366 | -9 99999 367 | -- 368 | 09 369 | -9 370 | -- 371 | 09 372 | -9 373 | -- 374 | 09 375 | -9 376 | -- 377 | 09 378 | -9 379 | -- 380 | 0 381 | """ 382 | ) 383 | 384 | File("temp.txt").delete() 385 | } 386 | } -------------------------------------------------------------------------------- /tutorial/extra.adoc: -------------------------------------------------------------------------------- 1 | = Дополнительные главы 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(val groups: List) { 183 | 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 | 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 | -------------------------------------------------------------------------------- /src/lesson8/task1/Files.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson8.task1 3 | 4 | import java.io.File 5 | 6 | /** 7 | * Пример 8 | * 9 | * Во входном файле с именем inputName содержится некоторый текст. 10 | * Вывести его в выходной файл с именем outputName, выровняв по левому краю, 11 | * чтобы длина каждой строки не превосходила lineLength. 12 | * Слова в слишком длинных строках следует переносить на следующую строку. 13 | * Слишком короткие строки следует дополнять словами из следующей строки. 14 | * Пустые строки во входном файле обозначают конец абзаца, 15 | * их следует сохранить и в выходном файле 16 | */ 17 | fun alignFile(inputName: String, lineLength: Int, outputName: String) { 18 | val outputStream = File(outputName).bufferedWriter() 19 | var currentLineLength = 0 20 | for (line in File(inputName).readLines()) { 21 | if (line.isEmpty()) { 22 | outputStream.newLine() 23 | if (currentLineLength > 0) { 24 | outputStream.newLine() 25 | currentLineLength = 0 26 | } 27 | continue 28 | } 29 | for (word in line.split(" ")) { 30 | if (currentLineLength > 0) { 31 | if (word.length + currentLineLength >= lineLength) { 32 | outputStream.newLine() 33 | currentLineLength = 0 34 | } 35 | else { 36 | outputStream.write(" ") 37 | currentLineLength++ 38 | } 39 | } 40 | outputStream.write(word) 41 | currentLineLength += word.length 42 | } 43 | } 44 | outputStream.close() 45 | } 46 | 47 | /** 48 | * Средняя 49 | * 50 | * Во входном файле с именем inputName содержится некоторый текст. 51 | * На вход подаётся список строк substrings. 52 | * Вернуть ассоциативный массив с числом вхождений каждой из строк в текст. 53 | * Регистр букв игнорировать, то есть буквы е и Е считать одинаковыми. 54 | * 55 | */ 56 | fun countSubstrings(inputName: String, substrings: List): Map { 57 | TODO() 58 | } 59 | 60 | 61 | /** 62 | * Средняя 63 | * 64 | * В русском языке, как правило, после букв Ж, Ч, Ш, Щ пишется И, А, У, а не Ы, Я, Ю. 65 | * Во входном файле с именем inputName содержится некоторый текст на русском языке. 66 | * Проверить текст во входном файле на соблюдение данного правила и вывести в выходной 67 | * файл outputName текст с исправленными ошибками. 68 | * 69 | * Регистр заменённых букв следует сохранять. 70 | * 71 | * Исключения (жюри, брошюра, парашют) в рамках данного задания обрабатывать не нужно 72 | * 73 | */ 74 | fun sibilants(inputName: String, outputName: String) { 75 | TODO() 76 | } 77 | 78 | /** 79 | * Средняя 80 | * 81 | * Во входном файле с именем inputName содержится некоторый текст (в том числе, и на русском языке). 82 | * Вывести его в выходной файл с именем outputName, выровняв по центру 83 | * относительно самой длинной строки. 84 | * 85 | * Выравнивание следует производить путём добавления пробелов в начало строки. 86 | * 87 | * 88 | * Следующие правила должны быть выполнены: 89 | * 1) Пробелы в начале и в конце всех строк не следует сохранять. 90 | * 2) В случае невозможности выравнивания строго по центру, строка должна быть сдвинута в ЛЕВУЮ сторону 91 | * 3) Пустые строки не являются особым случаем, их тоже следует выравнивать 92 | * 4) Число строк в выходном файле должно быть равно числу строк во входном (в т. ч. пустых) 93 | * 94 | */ 95 | fun centerFile(inputName: String, outputName: String) { 96 | TODO() 97 | } 98 | 99 | /** 100 | * Сложная 101 | * 102 | * Во входном файле с именем inputName содержится некоторый текст (в том числе, и на русском языке). 103 | * Вывести его в выходной файл с именем outputName, выровняв по левому и правому краю относительно 104 | * самой длинной строки. 105 | * Выравнивание производить, вставляя дополнительные пробелы между словами: равномерно по всей строке 106 | * 107 | * Слова внутри строки отделяются друг от друга одним или более пробелом. 108 | * 109 | * Следующие правила должны быть выполнены: 110 | * 1) Каждая строка входного и выходного файла не должна начинаться или заканчиваться пробелом. 111 | * 2) Пустые строки или строки из пробелов выводятся трансформируются в пустые строки без пробелов. 112 | * 3) Строки из одного слова выводятся без пробелов. 113 | * 3) Число строк в выходном файле должно быть равно числу строк во входном (в т. ч. пустых). 114 | * 115 | * Равномерность определяется следующими формальными правилами: 116 | * 1) Число пробелов между каждыми двумя парами соседних слов не должно отличаться более, чем на 1. 117 | * 2) Число пробелов между более левой парой соседних слов должно быть больше или равно числу пробелов 118 | * между более правой парой соседних слов. 119 | * 120 | */ 121 | fun alignFileByWidth(inputName: String, outputName: String) { 122 | TODO() 123 | } 124 | 125 | /** 126 | * Средняя 127 | * 128 | * Во входном файле с именем inputName содержится некоторый текст (в том числе, и на русском языке). 129 | * 130 | * Вернуть ассоциативный массив, содержащий 20 наиболее часто встречающихся слов с их количеством. 131 | * Если в тексте менее 20 различных слов, вернуть все слова. 132 | * 133 | * Словом считается непрерывная последовательность из букв (русских, либо латинских, без знаков препинания и цифр). 134 | * Регистр букв игнорировать, то есть буквы е и Е считать одинаковыми. 135 | * Ключи в ассоциативном массиве должны быть в нижнем регистре. 136 | * 137 | */ 138 | fun top20Words(inputName: String): Map { 139 | TODO() 140 | } 141 | 142 | /** 143 | * Средняя 144 | * 145 | * Реализовать транслитерацию текста из входного файла в выходной файл посредством динамически задаваемых правил. 146 | 147 | * Во входном файле с именем inputName содержится некоторый текст (в том числе, и на русском языке). 148 | * 149 | * В ассоциативном массиве dictionary содержится словарь, в котором некоторым символам 150 | * ставится в соответствие строчка из символов, например 151 | * mapOf('з' to "zz", 'р' to "r", 'д' to "d", 'й' to "y", 'М' to "m", 'и' to "yy", '!' to "!!!") 152 | * 153 | * Необходимо вывести в итоговый файл с именем outputName 154 | * содержимое текста с заменой всех символов из словаря на соответствующие им строки. 155 | * 156 | * При этом регистр символов в словаре должен игнорироваться, 157 | * но при выводе символ в верхнем регистре отображается в строку, начинающуюся с символа в верхнем регистре. 158 | * 159 | * Пример. 160 | * Входной текст: Здравствуй, мир! 161 | * 162 | * заменяется на 163 | * 164 | * Выходной текст: Zzdrавствуy, mир!!! 165 | * 166 | * Обратите внимание: данная функция не имеет возвращаемого значения 167 | */ 168 | fun transliterate(inputName: String, dictionary: Map, outputName: String) { 169 | TODO() 170 | } 171 | 172 | /** 173 | * Средняя 174 | * 175 | * Во входном файле с именем inputName имеется словарь с одним словом в каждой строчке. 176 | * Выбрать из данного словаря наиболее длинное слово, 177 | * в котором все буквы разные, например: Неряшливость, Четырёхдюймовка. 178 | * Вывести его в выходной файл с именем outputName. 179 | * Если во входном файле имеется несколько слов с одинаковой длиной, в которых все буквы разные, 180 | * в выходной файл следует вывести их все через запятую. 181 | * Регистр букв игнорировать, то есть буквы е и Е считать одинаковыми. 182 | * 183 | * Пример входного файла: 184 | * Карминовый 185 | * Боязливый 186 | * Некрасивый 187 | * Остроумный 188 | * БелогЛазый 189 | * ФиолетОвый 190 | 191 | * Соответствующий выходной файл: 192 | * Карминовый, Некрасивый 193 | * 194 | * Обратите внимание: данная функция не имеет возвращаемого значения 195 | */ 196 | fun chooseLongestChaoticWord(inputName: String, outputName: String) { 197 | TODO() 198 | } 199 | 200 | /** 201 | * Сложная 202 | * 203 | * Реализовать транслитерацию текста в заданном формате разметки в формат разметки HTML. 204 | * 205 | * Во входном файле с именем inputName содержится текст, содержащий в себе элементы текстовой разметки следующих типов: 206 | * - *текст в курсивном начертании* -- курсив 207 | * - **текст в полужирном начертании** -- полужирный 208 | * - ~~зачёркнутый текст~~ -- зачёркивание 209 | * 210 | * Следует вывести в выходной файл этот же текст в формате HTML: 211 | * - текст в курсивном начертании 212 | * - текст в полужирном начертании 213 | * - зачёркнутый текст 214 | * 215 | * Кроме того, все абзацы исходного текста, отделённые друг от друга пустыми строками, следует обернуть в теги

...

, 216 | * а весь текст целиком в теги .... 217 | * 218 | * Все остальные части исходного текста должны остаться неизменными с точностью до наборов пробелов и переносов строк. 219 | * Отдельно следует заметить, что открывающая последовательность из трёх звёздочек (***) должна трактоваться как "" 220 | * и никак иначе. 221 | * 222 | * Пример входного файла: 223 | Lorem ipsum *dolor sit amet*, consectetur **adipiscing** elit. 224 | Vestibulum lobortis, ~~Est vehicula rutrum *suscipit*~~, ipsum ~~lib~~ero *placerat **tortor***, 225 | 226 | Suspendisse ~~et elit in enim tempus iaculis~~. 227 | * 228 | * Соответствующий выходной файл: 229 | 230 | 231 |

232 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 233 | Vestibulum lobortis. Est vehicula rutrum suscipit, ipsum libero placerat tortor. 234 |

235 |

236 | Suspendisse et elit in enim tempus iaculis. 237 |

238 | 239 | 240 | * 241 | * (Отступы и переносы строк в примере добавлены для наглядности, при решении задачи их реализовывать не обязательно) 242 | */ 243 | fun markdownToHtmlSimple(inputName: String, outputName: String) { 244 | TODO() 245 | } 246 | 247 | /** 248 | * Сложная 249 | * 250 | * Реализовать транслитерацию текста в заданном формате разметки в формат разметки HTML. 251 | * 252 | * Во входном файле с именем inputName содержится текст, содержащий в себе набор вложенных друг в друга списков. 253 | * Списки бывают двух типов: нумерованные и ненумерованные. 254 | * 255 | * Каждый элемент ненумерованного списка начинается с новой строки и символа '*', каждый элемент нумерованного списка -- 256 | * с новой строки, числа и точки. Каждый элемент вложенного списка начинается с отступа из пробелов, на 4 пробела большего, 257 | * чем список-родитель. Максимально глубина вложенности списков может достигать 6. "Верхние" списки файла начинются 258 | * прямо с начала строки. 259 | * 260 | * Следует вывести этот же текст в выходной файл в формате HTML: 261 | * Нумерованный список: 262 | *
    263 | *
  1. Раз
  2. 264 | *
  3. Два
  4. 265 | *
  5. Три
  6. 266 | *
267 | * 268 | * Ненумерованный список: 269 | *
    270 | *
  • Раз
  • 271 | *
  • Два
  • 272 | *
  • Три
  • 273 | *
274 | * 275 | * Кроме того, весь текст целиком следует обернуть в теги ... 276 | * 277 | * Все остальные части исходного текста должны остаться неизменными с точностью до наборов пробелов и переносов строк. 278 | * 279 | * Пример входного файла: 280 | ///////////////////////////////начало файла///////////////////////////////////////////////////////////////////////////// 281 | * Утка по-пекински 282 | * Утка 283 | * Соус 284 | * Салат Оливье 285 | 1. Мясо 286 | * Или колбаса 287 | 2. Майонез 288 | 3. Картофель 289 | 4. Что-то там ещё 290 | * Помидоры 291 | * Фрукты 292 | 1. Бананы 293 | 23. Яблоки 294 | 1. Красные 295 | 2. Зелёные 296 | ///////////////////////////////конец файла////////////////////////////////////////////////////////////////////////////// 297 | * 298 | * 299 | * Соответствующий выходной файл: 300 | ///////////////////////////////начало файла///////////////////////////////////////////////////////////////////////////// 301 | 302 | 303 |
    304 |
  • 305 | Утка по-пекински 306 |
      307 |
    • Утка
    • 308 |
    • Соус
    • 309 |
    310 |
  • 311 |
  • 312 | Салат Оливье 313 |
      314 |
    1. Мясо 315 |
        316 |
      • 317 | Или колбаса 318 |
      • 319 |
      320 |
    2. 321 |
    3. Майонез
    4. 322 |
    5. Картофель
    6. 323 |
    7. Что-то там ещё
    8. 324 |
    325 |
  • 326 |
  • Помидоры
  • 327 |
  • 328 | Яблоки 329 |
      330 |
    1. Красные
    2. 331 |
    3. Зелёные
    4. 332 |
    333 |
  • 334 |
335 | 336 | 337 | ///////////////////////////////конец файла////////////////////////////////////////////////////////////////////////////// 338 | * (Отступы и переносы строк в примере добавлены для наглядности, при решении задачи их реализовывать не обязательно) 339 | */ 340 | fun markdownToHtmlLists(inputName: String, outputName: String) { 341 | TODO() 342 | } 343 | 344 | /** 345 | * Очень сложная 346 | * 347 | * Реализовать преобразования из двух предыдущих задач одновременно над одним и тем же файлом. 348 | * Следует помнить, что: 349 | * - Списки, отделённые друг от друга пустой строкой, являются разными и должны оказаться в разных параграфах выходного файла. 350 | * 351 | */ 352 | fun markdownToHtml(inputName: String, outputName: String) { 353 | TODO() 354 | } 355 | 356 | /** 357 | * Средняя 358 | * 359 | * Вывести в выходной файл процесс умножения столбиком числа lhv (> 0) на число rhv (> 0). 360 | * 361 | * Пример (для lhv == 19935, rhv == 111): 362 | 19935 363 | * 111 364 | -------- 365 | 19935 366 | + 19935 367 | +19935 368 | -------- 369 | 2212785 370 | * Используемые пробелы, отступы и дефисы должны в точности соответствовать примеру. 371 | * Нули в множителе обрабатывать так же, как и остальные цифры: 372 | 235 373 | * 10 374 | ----- 375 | 0 376 | +235 377 | ----- 378 | 2350 379 | * 380 | */ 381 | fun printMultiplicationProcess(lhv: Int, rhv: Int, outputName: String) { 382 | TODO() 383 | } 384 | 385 | 386 | /** 387 | * Очень сложная 388 | * 389 | * Вывести в выходной файл процесс деления столбиком числа lhv (> 0) на число rhv (> 0). 390 | * 391 | * Пример (для lhv == 19935, rhv == 22): 392 | 19935 | 22 393 | -198 906 394 | ---- 395 | 13 396 | -0 397 | -- 398 | 135 399 | -132 400 | ---- 401 | 3 402 | 403 | * Используемые пробелы, отступы и дефисы должны в точности соответствовать примеру. 404 | * 405 | */ 406 | fun printDivisionProcess(lhv: Int, rhv: Int, outputName: String) { 407 | TODO() 408 | } 409 | 410 | -------------------------------------------------------------------------------- /tutorial/chapter04_5.adoc: -------------------------------------------------------------------------------- 1 | = 4.5. Хранение данных в памяти компьютера 2 | 3 | В этом разделе мы поговорим о том, как различные элементы программы сохраняются в памяти PC. 4 | Поскольку данное пособие посвящено Котлину как JVM-языку, они сохраняются в памяти JVM как виртуального компьютера. 5 | Тем не менее, организация памяти JVM имеет немало общего с организацией памяти настоящего компьютера. 6 | Поговорим об этом подробнее. 7 | 8 | == Биты, байты и килобайты 9 | 10 | Единицей измерения информации (например, хранящейся в памяти или на диске) является __бит__ -- 11 | элементарная ячейка, имеющая всего два состояния (0 и 1, либо **true** и **false**). 12 | Биты объединяеются в __байты__ -- в одном байте 2^3^ = 8 бит. 13 | Байты, в свою очередь, объединяются в __килобайты__ (Кб) -- в одном килобайте 2^10^ = 1024 байт. 14 | Обратите внимание -- не 1000 байт, а именно 1024 байт, 15 | поскольку компьютеру удобнее, чтобы данные размерности были степенями двойки. 16 | В свою очередь, в одном мегабайте (Мб) 1024 Кб, в одном гигабайте (Гб) 1024 Мб, в одном терабайте (Тб) 1024 Гб. 17 | 18 | == Системы счисления 19 | 20 | При хранении чисел в компьютере используется не привычная человеку десятичная система счисления, 21 | а более удобная для компьютера двоичная система. При этом используется всего две цифры: 0 и 1. 22 | К примеру, число 75 в двоичной системе представляется как 23 | `1001011 = 1 х 64 + 0 х 32 + 0 х 16 + 1 x 8 + 0 x 4 + 1 x 2 + 1 x 1`. 24 | 25 | Поскольку двоичная запись числа является очень длинной, иногда (для удобства связи между человеком и компьютером) 26 | программисты применяют восьмеричную системму счисления (в ней восемь цифр от 0 до 7) 27 | и шестнадцатеричную систему счисления (в ней, помимо традиционных цифр от 0 до 9, 28 | используются цифры a = 10, b = 11, c = 12, d = 13, e = 14, f = 15). 29 | Удобство состоит в том, что одной восьмеричной цифре соответствует ровно три двоичных -- к примеру, 5 = 101, 30 | а одной шестнадцатеричной -- ровно четыре двоичных, к примеру, b = 1011. 31 | 75 представляется как 113 в восьмеричной и как 4b в двоичной системе счисления. 32 | 33 | Шестнадцатеричную константу в Котлине можно записать с префиксом `0x`: к примеру, `0x4b`. 34 | Восьмеричные константы в Котлине не поддерживаются, а двоичные начинаются с префикса `0b`: к примеру, `0b1001011`. 35 | Подробнее о системах счисления и способах перевода чисел из одной системы в другую можно прочитать в статьях Википедии 36 | "Система счисления" и "Позиционная система счисления". 37 | 38 | == Целые числа 39 | 40 | Целые числа типа **Int** занимают в памяти четыре байта, или 32 бита, 41 | при хранении используется двоичная система счисления. 42 | Модуль числа занимает 31 бит из 32, 43 | старший бит занимает знак (он равен 0 для положительных и -1 для отрицательных чисел). 44 | Модуль положительных чисел хранится обычным образом, а отрицательных -- в так называемом __дополнительном коде__. 45 | При этом вместо модуля числа `|x|` в памяти хранится значение `2^31^ - |x|`, 46 | например, `2^31^ - 1` для числа `-1` или `0` для числа `-2^31^`. 47 | Использование дополнительного кода позволяет упростить операции сложения и вычитания чисел для компьютера. 48 | 49 | Помимо типа **Int**, в Котлине имеется ещё три целочисленных типа, а именно: 50 | 51 | * **Byte** -- целое число от -128 до 127, занимает один байт; 52 | * **Short** -- целое число от -32768 до 32767, занимает два байта; 53 | * **Long** -- целое число от `-2^63^` до `2^63^ - 1`, занимает восемь байт. 54 | 55 | Подобные целые числа устроены по тому же принципу, что и числа типа **Int**. 56 | Следует иметь в виду, что при работе с типом **Long** нельзя использовать литералы типа **Int**: 57 | 58 | [source,kotlin] 59 | ---- 60 | val long1: Long = 42 // Error! 61 | val long2: Long = 42L // OK! 62 | ---- 63 | 64 | Вместо них используются литералы типа **Long** с суффиксом `L`. 65 | 66 | Как правило, в программах используется именно тип **Int** для хранения целых чисел. 67 | Исключения составляют следующие случаи: 68 | 69 | * когда диапазон типа **Int** недостаточен для целей программы, следует использовать **Long**; 70 | * когда требуется сохранить __большое__ (измеряемое, минимум, десятками тысяч) количество чисел с маленьким диапазоном -- следует использовать **Byte** или **Short** для экономии памяти. 71 | 72 | == Вещественные числа 73 | 74 | Вещественные числа хранятся в памяти в так называемом __экспоненциальном__ формате `M * 2^E^`. 75 | При этом часть бит выделяется на хранение мантиссы `M` (она находится в ограниченном диапазоне, обычно от 0.5 до 1), 76 | а другая часть бит -- на хранение порядка `E`. 77 | Как мантисса, так и порядок хранятся в двоичной системе счисления, причём один бит всегда выделяется под знак. 78 | 79 | В Котлине имеется два вещественных типа. 80 | Один из них, уже известный нам **Double**, занимает в памяти восемь байт. 81 | При этом 53 бита выделяется на мантиссу, а 11 бит на порядок. 82 | Более короткий тип **Float** занимает четыре байта: 24 бита на мантиссу и 8 бит на порядок. 83 | 84 | Вещественные литералы по умолчанию имеют тип **Double**. 85 | Для того, чтобы создать литерал типа **Float**, необходимо использовать суффикс `F`: 86 | 87 | [source,kotlin] 88 | ---- 89 | val f1: Float = 1.0 // Error! 90 | val f2: Float = 1.0F // OK! 91 | ---- 92 | 93 | Тип **Double** является рекомендуемым для хранения вещественных чисел. 94 | Тип **Float** следует использовать, если чисел требуется много, а его точности достаточно. 95 | 96 | == Символы 97 | 98 | Символы в Котлине используют тип **Char** и занимают в памяти два байта. 99 | Вместо символа в памяти хранится его __код__ -- номер данного символа в таблице кодировки Юникод. 100 | Тип **Char** поддерживает только символы из так называемого базового многоязыкового диапазона (Basic Multilingual Plane), 101 | номера которых находятся в интервале от 0 до 2^16^ - 1. 102 | Дополнительные символы с номерами более 2^16^ - 1 могут быть использованы следующим образом: 103 | 104 | * для хранения кода символа может быть использован тип **Int**; 105 | * для представления такого символа внутри строки используются два элемента типа **Char**, идущих подряд. 106 | 107 | Напомним, что символьные константы в Котлине записываются в одинарных кавычках. 108 | Для представления специальных символов используется экранирование, например: 109 | 110 | * `'\t'` -- табуляция; 111 | * `'\n'` -- новая строка; 112 | * `'\r'` -- возврат каретки (этот и предыдущий символ остались от эпохи пишущих машинок, которым, чтобы начать вывод с новой строки, было необходимо выполнить две операции -- __возврат каретки__ к началу строки и перевод каретки на __новую строку__); 113 | * `'\''` -- одинарная кавычка; 114 | * `'\"'` -- двойная кавычка; 115 | * `'\\'` -- обратный слэш; 116 | * `'\$'` -- доллар. 117 | 118 | Символы, отсутствующие на клавиатуре, могут быть также заданы с помощью шестнадцатеричного номера в таблице Юникод, 119 | например, `'\uFF00'` -- символ с номером `FF00`. 120 | 121 | == Значения и ссылки 122 | 123 | В Котлине существует два способа хранения переменных (параметров) в памяти JVM: хранение значений и хранение ссылок. 124 | В любом из этих способов для переменной выделяется ячейка памяти, размер которой зависит от типа переменной, 125 | но не превышает 8 байт. 126 | 127 | При хранении __значений__ в эту ячейку помещается значение переменной -- 128 | так обычно (строго говоря, не всегда) происходит с переменными целочисленного, вещественного и символьного типа. 129 | При изменении значения переменной изменяется и содержимое соответствующей ей ячейки. 130 | 131 | При хранении __ссылок__ в ячейку переменной помещается ссылка, 132 | при этом значение (содержимое) переменной хранится в специальном участке памяти JVM -- __куче__ (heap). 133 | Каждому используемому участку памяти кучи соответствует определённый номер, 134 | и как раз этот номер и используется в качестве __ссылки__. 135 | То есть, при хранении ссылок для чтения значения переменной необходимо выполнить не одно, а два действия: 136 | 137 | * прочитать номер участка в куче из ячейки переменной; 138 | * по этому номеру обратиться к куче и прочитать значение переменной. 139 | 140 | Хранение ссылок используется для всех составных и нестандартных типов, в частности, для строк, массивов, списков. 141 | При изменении переменной в результате выполнения оператора вроде `v = ...` изменяется ссылка. Например: 142 | 143 | [source,kotlin] 144 | ---- 145 | fun foo() { 146 | // [1, 2, 3] хранится в участке кучи с номером 1, a хранит номер 1 147 | val a = listOf(1, 2, 3) 148 | // [4, 5] хранится в участке кучи с номером 2, b хранит номер 2 149 | var b = listOf(4, 5) 150 | // Присваивание ссылок: b теперь хранит номер 1 151 | b = a 152 | } 153 | ---- 154 | 155 | Обратите внимание, что после выполнения трёх приведённых операторов в участке кучи с номером 2 хранится список [4, 5], 156 | но ни одна переменная не хранит ссылку на этот участок. 157 | Подобный участок через некоторое время будет найден и уничтожен специальной программой JVM -- 158 | __сборщиком мусора__, он же Garbage Collector. 159 | 160 | Такие типы, как **String** или **List**, не предполагают возможность изменения **содержимого** переменной. 161 | Опять-таки при попытке выполнить оператор вида `s = ...` изменится ссылка. Например: 162 | 163 | [source,kotlin] 164 | ---- 165 | fun foo() { 166 | // Alpha: участок с номером 1 167 | val a = "Alpha" 168 | // Beta: участок с номером 2 169 | var b = "Beta" 170 | // Тоже номер 2 171 | val c = b 172 | // Формируем Alpha + Beta = AlphaBeta: участок с номером 3 173 | b = a + b 174 | } 175 | ---- 176 | 177 | При сложении `a` и `b` будет создана новая строка AlphaBeta и размещена в участке памяти с номером 3. 178 | После этого номер 3 будет записан в переменную `b`. Отметьте, что `c` по-прежнему хранит номер 2, а `a` -- номер 1. 179 | 180 | Особенно интересна ситуация с типом **MutableList**, который позволяет изменять и содержимое переменной тоже. 181 | Например: 182 | 183 | [source,kotlin] 184 | ---- 185 | fun foo() { 186 | // Участок с номером 1 187 | val a = mutableListOf(1, 2, 3) 188 | // Тоже номер 1 189 | val b = a 190 | // Изменение содержимого участка с номером 1: теперь это [1, 2, 5] 191 | b[2] = 5 192 | println(a[2]) // 5 (!) 193 | } 194 | ---- 195 | 196 | После выполнения оператора `b[2] = 5` участок памяти с номером 1 будет хранить список `[1, 2, 5]`. 197 | Поскольку в переменной `a` хранится тот же номер 1, то вывод на консоль `a[2]` приведёт к выводу числа 5, 198 | хотя раньше этот элемент списка хранил значение 3. 199 | 200 | Подобный принцип используют и функции, имеющие параметр с типом **MutableList**: 201 | 202 | [source,kotlin] 203 | ---- 204 | fun invertPositives(list: MutableList) { 205 | for (i in 0..list.size - 1) { 206 | val element = list[i] 207 | if (element > 0) { 208 | list[i] = -element 209 | } 210 | } 211 | } 212 | 213 | fun test() { 214 | // Участок номер 1 215 | val a = mutableListOf(1, -2, 3) 216 | invertPositives(a) 217 | println(a) // [-1, -2, -3] 218 | } 219 | ---- 220 | 221 | При вызове `invertPositives` номер 1 будет переписан из аргумента `a` в параметр `list`. 222 | После этого функция `invertPositives` изменит содержимое списка, используя данный номер, 223 | и вызов `println(a)` выведет `[-1, -2, -3]` на консоль. 224 | 225 | Таким образом, имея дело с типами, хранящимися по ссылке (чаще говорят проще -- __ссылочные типы__), 226 | стоит различать действия со ссылками и действия со значениями. 227 | К примеру, присваивание `name = ...` -- это всегда действие со ссылкой. 228 | С другой стороны, вызов функции вроде `list.isEmpty()` или индексация вроде `list[i]`, `list[j] = i` -- 229 | это действия с содержимым, причём, некоторые из этих действий только читают содержимое переменной, 230 | а некоторые другие -- изменяют его. 231 | 232 | С учётом этого различия в Котлине определено две разных операции сравнения на равенство: 233 | уже известная нам `==` и новая `===`. 234 | Операция `a == b` -- это сравнение содержимого на равенство, которое обычно выполняется 235 | с помощью вызова функции `a.equals(b)` -- про неё мы поговорим в разделе 7. 236 | Операция `a === b` -- это сравнение ссылок на равенство, для которого не имеет значения, 237 | одинаковое содержимое у переменных или нет, важно только, чтобы оно находилось в участке кучи с одинаковым номером. 238 | Например: 239 | 240 | [source,kotlin] 241 | ---- 242 | fun foo() { 243 | val a = listOf(1, 2) 244 | val b = listOf(1, 2) 245 | println(a == b) // true 246 | println(a === b) // false 247 | } 248 | ---- 249 | 250 | Здесь `a` и `b` имеют одно и то же содержимое, но находятся в участках кучи с разными номерами. 251 | Операция `!=` обратна операции `==` (сравнение содержимого на неравенство), 252 | а операция `!==`, соответственно -- обратна операции `===` (сравнение ссылок на неравенство). 253 | 254 | Важно также, что сравнение содержимого на равенство не реализовано для массивов **Array**, 255 | и поэтому для них операции `==` и `===` эквивалентны. 256 | Это одна из причин, по которой следует использовать списки вместо массивов, где это возможно. 257 | Пример: 258 | 259 | [source,kotlin] 260 | ---- 261 | fun foo() { 262 | val a = arrayOf(1, 2) 263 | val b = arrayOf(1, 2) 264 | println(a == b) // false (!) 265 | println(a === b) // false 266 | } 267 | ---- 268 | 269 | == Организация памяти JVM 270 | 271 | При запуске программы, написанной на Java или Котлине, 272 | все элементы программы хранятся в различных участках памяти виртуальной машины Java. 273 | Технически, JVM память может быть разбита на четыре участка: 274 | 275 | * участок для хранения функций (хранит байт-коды всех имеющихся в программе функций); 276 | * участок для хранения констант (хранит строковые литералы и значения переменных, известные во время компиляции) 277 | * куча (хранит значения для переменных ссылочного типа) 278 | * стек (хранит локальные переменные и параметры -- ссылки, если их тип ссылочный и значения в противном случае) 279 | 280 | Участки для хранения функций и констант во время выполнения программы не меняются или почти не меняются. 281 | В куче по мере необходимости (при создании или изменении строк, списков, ...) создаются новые участки памяти. 282 | Эти участки разрушаются сборщиком мусора, когда их перестают использовать. 283 | 284 | Стек по принципу работы подобен магазину автомата. Рассмотрим пример: 285 | 286 | [source,kotlin] 287 | ---- 288 | fun bar(x: Int, y: Int): Int { 289 | // Stack: main::args, foo::n, bar::x, bar::y 290 | val z = x + y 291 | // Stack: main::args, foo::n, bar::x, bar::y, bar::z 292 | return z 293 | } 294 | 295 | fun foo(n: Int): Int { 296 | // Stack: main::args, foo::n 297 | return bar(n / 2, n * 2) 298 | } 299 | 300 | fun main(args: Array) { 301 | // Stack: main::args 302 | foo(8) 303 | } 304 | ---- 305 | 306 | Здесь изначально стек хранил только параметр `args`, объявленный в функции `main` и содержащий ссылку на массив. 307 | После вызова `foo` в стеке появляется её параметр `n`, а после вызова `bar` -- её параметры `x` и `y`. 308 | Поскольку все эти параметры целые, хранятся их значения, а не ссылки. 309 | Затем определяется промежуточная переменная `z`, которая тоже попадает в стек. 310 | 311 | По окончании работы функции `bar` из стека удаляются верхние переменные `x`, `y`, `z`, 312 | а после окончания работы `foo` -- также переменная `n`. 313 | Таким образом, стек делается больше или меньше в процессе работы функций. 314 | --------------------------------------------------------------------------------