Приложение на Kotlin

Приложение на Kotlin #

Задача № 1 #

В рамках данной практической работы реализуйте небольшое консольное приложение на языке Kotlin, решающее следующую задачу.

Пользователь вводит в приложение коэффициенты многочлена произвольной степени n. Многочлен степени n имеет форму \( a_n * x^n + a_{n−1} *x^{n−1} + · · · + a_1 * x + a_0 \) .

Вычислите значение многочлена в точке x, указанной пользователем. Вычислите значение производной многочлена в той же точке.

Создание проекта в IDEA #

При создании проекта в IDEA необходимо настроить сборку с помощью системы сборки Gradle. Для этого запустите мастер по созданию нового проекта и выберите следующие пункты:

  1. Укажите название проекта и его местоположение.
  2. Выберите язык программирования (Language): Kotlin.
  3. Выберите систему сборки (Build system): Gradle.
  4. Выберите язык описания системы сборки (Gradle DSL): Groovy.
  5. Оставьте настройку по добавлению кода примера в новый проект (Add sample code).

Запуск приложения через Gradle #

При создании проекта в IDEA с использованием системы сборки Gradle в его конфигурацию не помещаются настройки для запуска приложения через Gradle. Эту настройку необходимо провести вручную.

Для запуска приложения необходимо настроить плагин application:

  1. Откройте файл build.gradle, расположенный в корне проекта.
  2. Добавьте плагин application в список настроенных плагинов. После добавления список плагинов должен выглядеть следующим образом:
    plugins {
      id 'org.jetbrains.kotlin.jvm' version '1.9.22'
      id 'application'
    }
  3. Укажите путь к модулю, содержащему функцию main, точку запуска приложения. По умолчанию им будет являться модуль Main.kt, входящий в пакет org.example. Данный модуль будет преобразован в бинарный класс org.example.MainKt. Таким образом в файл необходимо добавить:
    application {
        mainClass = "org.example.MainKt"
    }
  4. Синхронизируйте настройки Gradle с IDEA.

Чтение данных со стандартного потока ввода #

По умолчанию при использовании системы сборки Gradle при запуске приложения с помощью команды run у приложения нет доступа к стандартному потоку ввода. Решение этой проблемы детально описано в статье https://blog.thecodewhisperer.com/permalink/stdin-gradle-kotlin-dsl Краткая выжимка шагов:

  1. Откройте файл build.gradle
  2. Добавьте в файл
    run {
     standardInput = System.in
    }
     
  3. Перечитайте конфигурацию Gradle нажав на соответствующую кнопку в панели Gradle внутри среды разработки.

Для проверки работы ввода данных с консоли в файл Main.kt добавьте следующий код:

print("Put the data: ")
val data = readlnOrNull()
println("Got the '$data'")
После запуска приложения Вы должны уметь успешно считывать данные со стандартного потока ввода с использованием консольного ввода.

Предлагаемый подход к решению задачи #

  1. Реализуйте функцию для получения одного целого числа от пользователя со стандартного потока ввода. Данная функция может иметь следующую сигнатуру:
    fun readInt(prompt: String) : Int { /**/ }
    1. Аргумент prompt должен содержать строку-приглашение, например "Введите целое число> ", которое должно показываться пользователю перед вводом числа. Приглашение должно показываться перед вводом числа, без переноса строки. Используйте метод print.
    2. Для считывания рекомендуется использовать метод readLnOrNull.
    3. null-значение может быть получен в случае, например, в случае окончания потока ввода. В рамках данной программы будем считать, что поток ввода не завершён, и будем пытаться считывать данные со стандартного потока ввода ещё раз в случае получения null-значения.
    4. Метод должен получать строку от пользователя и пытаться преобразовать её к целому числу. Для преобразования строки к числу можно использовать метод toInt. Обратите внимание, что в случае ошибки преобразования будет выброшено исключение NumberFormatException. Процесс обработки исключений описан в документации.
    5. Если преобразование выполнено успешно, то его результат надо возвратить в качестве результата работы функции.
    6. Если преобразование не было успешно, то функция должна повторить запрос на ввод данных от пользователя. Пользователю необходимо сообщить о возникшей проблеме, чтобы он не повторил свою ошибку ещё раз.
  2. Реализуйте функцию для получения коэффициентов многочлена. Данная функция может иметь следующую сигнатуру:
    fun readCoefficients() : List<Int> { /**/ }
    1. Данная функция должна запрашивать степень коэффициента многочлена, N, с помощью метода readInt. Если было считано отрицательное число, то необходимо запросить ввод данных ещё раз.
    2. Затем функция должна получить N+1 коэффициентов многочлена. Для этого можно использовать метод readInt с сохранением результата в изменяемом списке целых чисел.
      1. Для создания динамического списка для хранения чисел используйте функцию mutableListOf().
      2. Для добавления элемента используйте метод add().
    3. Сформированный список коэффициентов многочлена метод должен вернуть в качестве своего значения.
    4. Созданный изменяемый список, объект интерфейса MutableList, может быть возвращён клиентам как объект неизменяемого списка List, т.к. интерфейс MutableList унаследован от List:
      interface MutableList<E> : List<E>, MutableCollection<E>
      Клиентам данного метода нет необходимости получать изменяемый список, т.к. пользователь уже завершил ввод своих данных.
    5. Удобным форматом для хранения коэффициентов является формат, когда на первом месте в массиве хранится коэффициент многочлена нулевой степени, на втором — первой степени и т.д. Для считывания удобно спрашивать у пользователя коэффициенты, начиная со старшего члена. Перед возвращением списка коэффициентов пользователю его необходимо развернуть с помощью метода reversed.
  3. Используйте функцию readInt для считывания точки x, в которой необходимо произвести вычисление значения многочлена.
  4. Реализуйте функцию для вычисления коэффициентов производной многочлена. Данная функция может иметь следующую сигнатуру:
    fun calcDerivative(coefs: List<Int>) : List <Int> { /**/ }
    1. Для вычисления производной удобно расположить коэффициенты многочлена по возрастающей. Т.е. коэффициент многочлена степени 0 должен быть первым, степени 1 - вторым и так далее.
    2. Производной будет многочлен, у которого:
      • Значение новых элементов вычисляется как значение коэффициента оригинального многочлена, умноженного на порядковый номер многочлена.
      • Первый элемент отсутствует, т.к. степень производной многочлена меньше степени многочлена на 1.
    3. Для вычисления производной по индексам можно воспользоваться диапазонами от 1 до размера оригинального массива:
      for (index in 1 until coefs.size) {
          coefs[index]
      }
  5. Реализовать функцию вычисления значения многочлена в точке. Данная функция может иметь следующую сигнатуру:
    fun polynomValue(coefs: List<Int>, point: Int) : Int { /**/ }
    1. Для вычисления значения числа в указанной степени можно воспользоваться методом pow.
    2. Для преобразования целого числа в вещественное можно использовать метод toFloat.
    3. Для преобразования вещественного числа в целое можно использовать метод toInt.
  6. Используя полученные функции выполнить вычисление значения многочлена и его производной в точке x.
  7. Выведите информацию о многочлене, его производной и их значении в указанной точке с помощью строковых шаблонов.

Задача № 2 #

Запишите информацию по результатам запуска приложения в JSON-документ.

  • Созданный документ должен находиться в каталоге results в корне проекта.
  • Название документа должно содержать информацию о текущем моменте времени в формате 2010-05-12-15-34-55.json.
  • Содержание JSON-документа проработайте самостоятельно. Однако числа необходимо записать именно в числовом формате.

Запись данных в JSON #

Для записи данных в JSON-документ воспользуемся библиотекой Jackson.

К проекту необходимо подключить библиотеку jackson-module-kotlin. Она в свою очередь зависит от уже известных нам jackson-databind, jackson-core и jackson-annotations. Для этого в файле build.gradle в разделе dependencies необходимо добавить следующую строчку:

implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.16.1'

Затем необходимо выполнить создание объекта com.fasterxml.jackson.databind.ObjectMapper с поддержкой работы с Kotlin-классами. Для решения этой задачи предоставляется удобная функция-конструктор jacksonObjectMapper():

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper

val objectMapper = jacksonObjectMapper()

Затем необходимо описать класс, объекты которого будем преобразовывать к строке. Возьмём, к примеру, следующий класс:

class Triangle(
    val sideA: Double,
    val sideB: Double,
    val sideC: Double,
)

Создадим объект данного класса и преобразуем его к строке:

val triangle = Triangle(4.0, 3.0, 5.0)
val string = objectMapper.writeValueAsString(triangle)

Работа с файлами #

Для работы с файлами и директориями воспользуйтесь уже известными Вам классами из пакета java.io, а также Kotlin-расширениям к ним.

Работа со временем #

Для получения текущего момента времени воспользуйтесь классом LocalDateTime.

Для преобразования объекта времени к строке воспользуйтесь классом DateTimeFormatter.

© A. M. Васильев, 2024, CC BY-SA 4.0, andrey@crafted.su