4. Основы Kotlin. Списки

Преобразование из списка в строку

Очень часто используемой в Котлине является сложная функция преобразования списка в строку joinToString(). Её заголовок выглядит примерно так:

fun <T> List<T>.joinToString(
        separator: String = ", ",
        prefix: String = "",
        postfix: String = "",
        limit: Int = -1,
        truncated: String = "...",
        transform: (T) -> String = { "$it" }
): String { ... }

Получателем данной функции может быть список с произвольным типом элементов: List<T>. Такая запись описывает так называемую настраиваемую функцию, о них мы будем говорить подробнее позже.

Все пять параметров этой функции имеют так называемые значения по умолчанию. Это значит, что при желании мы можем вызвать эту функцию вообще не указывая аргументов. Например, listOf(1, 2, 3).joinToString() даст нам следующий результат: "1, 2, 3" — выводя в строку все элементы списка через запятую. Возможна, однако, более тонкая настройка вывода:

  • параметр separator задаёт разделитель между элементами
  • параметр prefix задаёт строку, которая выводится перед самым первым элементом списка (префикс)
  • аналогично, параметр postfix задаёт строку, которая выводится после самого последнего элемента списка (постфикс)
  • параметр limit задаёт максимальное количество выводимых элементов. Значение -1 соответствует неограниченному количеству элементов, но, скажем, вызов listOf(1, 2, 3).joinToString(", ", "", "", 1) будет иметь результат "1, …​" вместо результата по умолчанию "1, 2, 3"
  • параметр truncated используется, если задан limit, и заменяет все элементы списка, которые не поместились в строке
  • параметр transform задаёт способ преобразования каждого из элементов списка в строку — по умолчанию это "$it" для элемента списка it, может быть изменён с помощью лямбды (см. функции высшего порядка выше)

Рассмотрим простой пример: необходимо написать функцию, которая по заданному списку целых чисел вида [3, 6, 5, 4, 9] сформирует строку, содержащую пример их суммирования: "3 + 6 + 5 + 4 + 9 = 26". На Котлине это записывается так:

fun buildSumExample(list: List<Int>) = list.joinToString(separator = " + ", postfix = " = ${list.sum()}")

В данном случае нам требуется вызов функции joinToString, все параметры которой имеют некоторые значения по умолчанию, то есть не требуют обязательного указания при вызове. Нам требуется указать разделитель чисел " + " и в конце вывода добавить знак = с приписанной к нему суммой чисел из списка. Для этого нам необходимо указать значения параметров separator и postfix, при этом остальные параметры мы указывать не хотим. В этом случае используются так называемые именованные аргументы, например: separator = " + ". Эта запись указывает, что аргумент " + " соответствует параметру функции separator. Если бы имена separator и postfix не указывались, возникла бы путаница, поскольку неясно, какому именно из строковых параметров функции соответствует тот или иной аргумент вызова.

Массивы

Массив Array — ещё один тип, предназначенный для хранения и модификации некоторого количества однотипных элементов. С точки зрения возможностей, массив похож на мутирующий список MutableList; главным их отличием является отсутствие возможности изменять свой размер — для массивов отсутствуют функции add и remove.

Для обращения к элементу массива служит оператор индексации: array[i], причём есть возможность как читать содержимое массива, так и изменять его. Для создания массива, удобно использовать функцию arrayOf(), аналогичную listOf() для списков.

Почти все возможности, имеющиеся для списков, имеются и для массивов тоже. Исключением являются функции для создания подсписков sublist. Также, массивы не следует сравнивать на равенство с помощью array1 == array2, поскольку во многих случаях такое сравнение даёт неверный результат (подробности про это — в разделе 4.5). Массив можно преобразовать к обычному списку с помощью array.toList() либо к мутирующему списку с помощью array.toMutableList(). Список, в свою очередь, можно преобразовать к массиву с помощью list.toTypedArray().

В целом, при написании программ на Котлине почти нет случаев, когда массивы использовать необходимо. Одним из немногих примеров является главная функция, параметр которой имеет тип Array<String> — через него в программу передаются аргументы командной строки.

Параметры переменной длины

В некоторых случаях бывает удобно при вызове функции не передавать ей аргумент типа List или Array, а просто перечислить элементы этого списка при вызове. Например, чтобы сформировать список квадратов чисел от 1 до 3 с помощью рассмотренной выше функции squares, нам пришлось бы вызвать данную функцию как squares(listOf(1, 2, 3)). Вызов выглядел бы проще без прямого указания listOf: просто как squares(1, 2, 3). Для поддержки этой возможности программисты придумали параметры переменной длины, в Котлине они называются vararg.

fun squares(vararg array: Int) = squares(array.toList())

При вызове подобной функции вместо параметра array может быть подставлено любое (в том числе ноль) количество аргументов указанного типа, в данном случае — Int.

Я не случайно назвал параметр именно array: функция может использовать такой параметр, как будто это массив соответствующего типа — в данном случае Array<Int>. В теле функции мы используем вызов array.toList(), чтобы свести задачу к уже решённой — функция squares, принимающая аргумент типа List<Int>, у нас уже имеется. Если необходимо подставить вместо vararg-параметра уже имеющийся массив, следует использовать оператор раскрытия*:

fun squares(array: Array<Int>) = squares(*array)

Здесь мы вызываем уже написанную функцию squares, принимающую параметр переменной длины.

В библиотеке Котлина имеется довольно много функций, имеющих vararg-параметр. Два классических примера были рассмотрены в этом разделе — это функции listOf(…​) и mutableListOf(…​).

Упражнения

Откройте файл srс/lesson4/task1/List.kt в проекте KotlinAsFirst. Выберите любую из задач в нём. Придумайте её решение и запишите его в теле соответствующей функции.

Откройте файл test/lesson4/task1/Tests.kt, найдите в нём тестовую функцию — её название должно совпадать с названием написанной вами функции. Запустите тестирование, в случае обнаружения ошибок исправьте их и добейтесь прохождения теста. Подумайте, все ли необходимые проверки включены в состав тестовой функции, добавьте в неё недостающие проверки.

Решите ещё хотя бы одну задачу из урока 4 на ваш выбор, попробуйте применить в процессе решения известные вам функции высшего порядка. Убедитесь в том, что можете решать такие задачи уверенно и без посторонней помощи. Попробуйте в решении хотя бы одной задачи применить функции высшего порядка.

По возможности решите одну из задач, помеченных как “Сложная” или “Очень Сложная”. Если вам потребуется преобразование списка в строку, примените list.joinToString(). Имейте в виду, что последняя задача, функция russian, действительно очень сложная, и для её решения может потребоваться значительное время.

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

Коментарі: 2
  1. Виктор Демихов
    Виктор Демихов

    Примеры не жизненные какие-то. Для математиков, а не для программистов.

    1. DegtjarenkoDW
      DegtjarenkoDW

      Прочел вашу фразу и вспомнил анекдот:
      Урок в первом классе :
      Учительница заносит и ставит на стол компьютер.
      – Дети сколько компьютеров на столе?
      – Один! – хором отвечают дети.
      Учительница заносит и ставит на стол еще один компьютер
      – Дети сколько компьютеров на столе?
      – Два! – хором отвечают дети.
      Идя за третьим компьютером учительница бурчит себе под нос :
      – с яблоками было легче.

Додати коментар