Как создать диапазон (range) в Kotlin, который считает в обратном порядке (от большего к меньшему)?
Почему 10..1 не работает?
В Kotlin оператор .. всегда создаёт диапазон с шагом +1. Если начальное значение больше конечного, результатом будет пустой диапазон (IntRange), так как компилятор не может автоматически изменить направление. Это сделано для безопасности — вы явно должны указать, что хотите обратный порядок.
Правильный способ: downTo
Для создания обратного диапазона используйте функцию downTo.
// Базовый пример
for (i in 10 downTo 1) {
println(i) // 10, 9, 8... 1
}
// С шагом больше 1
for (i in 10 downTo 1 step 2) {
println(i) // 10, 8, 6, 4, 2
}
// Сохраняем в переменную
val reverseRange: IntProgression = 10 downTo 1
for (i in reverseRange) {
println(i)
}
// Можно использовать несколько раз
println("Ещё раз:")
for (i in reverseRange) {
println(i)
}
Важные детали про IntProgression
Когда вы пишете 10 downTo 1, вы создаёте не IntRange, а IntProgression (прогрессию). В отличие от диапазона (IntRange), прогрессия может иметь отрицательный шаг. Вот ключевые отличия:
- IntRange — всегда возрастает, шаг = +1. Создаётся через
... - IntProgression — может убывать, шаг может быть любым. Создаётся через
downToилиstep.
Альтернативные подходы
1. Использование reversed()
Если у вас уже есть возрастающий диапазон, можно развернуть его:
val ascending = 1..10
for (i in ascending.reversed()) {
println(i) // 10, 9, 8... 1
}
// Или в одну строку:
for (i in (1..10).reversed()) {
println(i)
}
2. Использование until с downTo
Если нужно исключить последнее значение:
for (i in 10 downTo 1) {
// включает 1
}
for (i in 10 downTo 2) {
// не включает 1
}
Подводные камни
- Пустой диапазон:
for (i in 5..3)— ничего не выведет. Всегда используйтеdownToдля обратного порядка. - Тип переменной:
val r = 10 downTo 1— типIntProgression, а неIntRange. Это важно при передаче в функции. - Шаг должен быть положительным:
10 downTo 1 step -2— вызовет ошибку компиляции. Шаг всегда указывается как положительное число, а направление задаётсяdownTo. - Производительность:
reversed()создаёт новый объект-прогрессию, но это незначительные затраты для большинства случаев.
Итог
Для обратного перебора всегда используйте downTo. Это идиоматичный и эффективный способ в Kotlin. Если нужно сохранить диапазон для многократного использования — присвойте результат downTo переменной типа IntProgression. Избегайте оператора .. для обратных последовательностей, так как он создаёт пустой диапазон.
Дополнительный способ: цикл while
Хотя downTo — предпочтительный вариант, иногда полезно знать альтернативы. Например, можно использовать классический цикл while:
var i = 10
while (i >= 1) {
println(i)
i--
}
// Или с декрементом
var j = 10
while (j >= 1) {
println(j)
j -= 1
}
Использование библиотечных функций
Для коллекций можно применить forEach:
(10 downTo 1).forEach { println(it) }
// Или через индексы:
val list = listOf("a", "b", "c")
for (i in list.indices.reversed()) {
println("${list[i]} на позиции $i")
}
Когда это может пригодиться?
- Если вам нужно больше контроля над итерацией (например, изменять шаг динамически).
- При работе с коллекциями, где
indices.reversed()даёт понятный код. - В случаях, когда нужно прервать цикл по условию —
whileдаёт больше гибкости.
Однако для простого обратного перебора чисел downTo остаётся самым лаконичным и читаемым решением. Используйте его как основной инструмент.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Конкурентность и многопоточность в Kotlin: корутины, потоки и практические примеры