Освой Kotlin играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Интервалы

В Kotlin есть уникальные типы, которые вы не встречали в Java. Уверен, вам понравится.

Интерфейс ClosedRange<T> с методом rangeTo() позволяет быстро и элегантно создавать интервалы от начального до конечного значения, который является частью диапазона. На его основе созданы специальные типы IntRange, LongRange, CharRange.

Метод rangeTo() имеет операторную форму .. и может сопровождаться операторами in и !in.

Рассмотрим простой пример.


val oneToFive = 1..5
println(oneToFive::class.java) // возвращает class kotlin.ranges.IntRange

Выражение 1..5 можно заменить:


val oneToFive = IntRange(1, 5)
println(oneToFive) // 1..5

Альтернативная версия. Заодно покажу пример с символами.


val oneToFive: IntRange = 1.rangeTo(5)

// CharRange
val characters = 'a'.rangeTo('z') // all letters from 'a' to 'z'

Выберем случайное число из диапазона от 1 до 6 (игральный кубик).


val diceRange = 1..6
val randomNumber = diceRange.random()
println("Random number: ${randomNumber}")
println(diceRange::class)

Тип IntRange и его родственники имеют сверхспособности - проходить циклически по всем элементам. И это очень удобно.

Мы уже знакомились с оператором in в цикле for. Но он может быть использован и отдельно в функции. Например, мы можем проверить, входит ли символ в диапазон символов русского алфавита от символа к до символа т. Символ о точно попадёт в заданный диапазон, а также символы Л, М, Н и т.д.


fun isCat(c: Char) = c in 'к'..'т' || c in 'К'..'Т'
// проверяем символы в нижнем и верхнем регистрах
println(isCat('о'));

Пример с числами.


val number = 5 // проверим, входит ли число в заданный диапазон
println(number in 1..100) // true

Интервалы помогают сократить количество кода и улучшить читаемость.


// много кода
if (i >= 0 && i <= 10)
    println(i)

// меньше кода
if (i in 0..10)
    println(i)

По умолчанию, интервалы увеличиваются от меньшего к большему. Если нужно изменить порядок, то используйте downTo. Если нужно изменить шаг, то используйте ключевое слово step. Примеры показаны в for.

until

Очень часто в циклах встречается выражение size - 1, чтобы не выйти за пределы массива. Можно заменить на until, чтобы не использовать последний элемент интервала. Примеры показаны в for.

Функции для работы с интервалами

В Kotlin есть несколько готовых функций для работы с интервалами. Например, coerceIn(), вы задаёте интервал, если число не входит в него, то берётся минимальное или максимальное значение интервала.


fun showProgress(progress: Int){
    val percent = progress.coerceIn(0, 100)
    println("We're ${percent}% done")
}

showProgress(45)  // 45
showProgress(105) // 100

Узнать минимальное, максимальное значение интервала, а также сумму всех чисел в интервале можно через соответствующие функции.


val intRange: IntRange = 3..7
val min = intRange.minOrNull()
val max = intRange.maxOrNull()
val sum = intRange.sum()
println("Min: $min; Max: $max; Sum: $sum") // Min: 3; Max: 7; Sum: 25

Выбрать случайный элемент из интервала можно через random().


val charRange: CharRange = 'a'..'c'
val randomChar = charRange.random()
println(randomChar) // 'a' or 'b' or 'c'

Progression

Несколько классов для интервалов позволяют работать с элементами интервала просто и быстро. Пример для класса IntProgression.


val progression: IntProgression = 0..100 step 10
for (i in progression) {
    println(i)
}
/*
0
10
20
30
40
50
60
70
80
90
100
*/

Узнать величину шага step (в том числе значение по умолчанию, когда step не используется в явном виде) можно через соответствующие свойства.


val intProgression = 1..450 step 10
val intStep = intProgression.step
println(intStep) // 10

val longProgression = 10L downTo 1L
val longStep = longProgression.step
println(longStep) // -1

val charProgression = 'a'..'z'
val charStep = charProgression.step
println(charStep) // 1

Указав нужный интервал, можно перечислить его элементы в обратном порядке через метод reversed():


val fate: IntRange = 1..9
for (life in fate.reversed()) {
    println("Remaining lives: " +
            "$life")
}

Получим ответ.


Remaining lives: 9
Remaining lives: 8
Remaining lives: 7
Remaining lives: 6
Remaining lives: 5
Remaining lives: 4
Remaining lives: 3
Remaining lives: 2
Remaining lives: 1

Свойства first и last

Для объектов классов Range и Progression доступны свойства first и last, позволяющие узнать первые и последние элементы интервалов.


val intRange = 1..9
val first = intRange.first
println(first)

val progression: IntProgression = 0..100 step 10
println(progression.first)

Продвинутые программисты могу создать собственные интервалы на основе Range и Progression.

Реклама