Цикл for служит для перебора значений по каким-либо коллекциям, например, по спискам или по диапазонам чисел. В первой части расскажу про диапазоны (или интервалы), а затем перейдем к разбору конструкции for.
Диапазоны (интервалы)
Диапазоны – это такие объекты, которые содержат числа в заданном промежутке. Создать диапазоны можно с помощью двух точек (..) или ключевого слова until между двумя значениями. Разница между этими способами заключается лишь в том, что при использовании until в диапазон не включается последнее значение. Так создаются интервалы с нарастающим значением, где следующее значение больше предыдущего.
val range1 = 42..442
val range2 = 42 until 442
Диапазоны могут быть, соответственно, определенных типов. Например, Long, символный тип Char, Double и Float.
val range3 = 42L..442L
val range4 = 'a'..'z'
val range5 = 42.1..442.1
val range6 = 42.1f..442.1f
Для наглядности явно проставим типы данных у диапазонов.
Забегая вперед, скажу, что диапазоны с типом ClosedRange нельзя использовать для итерации по их значениям. Поэтому типы с плавающей точкой в циклах for мы использовать не будем. В случае с CharRange итерация по символам работать будет, потому что все символы “под капотом” кодируются в целые числа.
val range1: IntRange = 42 until 442
val range2: IntRange = 42..442
val range3: LongRange = 42L..442L
val range4: CharRange = 'a'..'z'
val range5: ClosedRange<Double> = 42.1..442.1
val range6: ClosedRange<Float> = 42.1f..442.1f
Функция step в Kotlin
Диапазоны в Kotlin также можно рассматривать как прогрессии. Если что, прогрессия – это просто ряд увеличивающихся или уменьшающихся чисел. У прогрессии в языке есть свои типы, например, IntProgression, CharProgression и так далее. Так вот, в интервале можно задать шаг, с которым мы будем итерироваться по этому диапазону с помощью функции step и этот диапазон автоматически станет прогрессией с собственным типом.
val range7 = 42 .. 442 step 2
По умолчанию step всегда равен 1. Мы можем указать его явно или присвоить другой шаг, например, 2. Также для наглядности выведем новый тип нашей переменной.
val range7: IntProgression = 42 .. 442 step 2
Функция downTo
С помощью двух точек (..) мы создавали интервалы со значениями по возрастанию. В случае, если необходимо объявить диапазон по убыванию, вместо точек используется функция downTo. И это еще один случай, когда IngRange становится типом IntProgression. Без учета наличия или отсутствия step.
val range8: IntProgression = 442 downTo 42 step 2
По прогрессиям это все, что надо понимать в рамках данной темы. По сути достаточно знать, что они могут существовать в таком виде.
Принадлежность к интервалу in, !in
Далее. С помощью операторов in и !in можно проверить принадлежность какого-то значения к интервалу. Напишем такой пример. Результат проверки будем сохранять в Boolean переменные a и b. in – вернет true, если объект имеется в диапазоне. !in – соответственно вернет true, если объект отсутствует в проверяемом интервале.
val a = 52 in range1
val b = 52 !in range1
println(a)
println(b)
Цикл for
Вернемся к циклам. В прошлом уроке мы делали обратный отсчет с распечаткой значения в консоль каждую секунду с помощью цикла while и do-while. Сейчас я хочу познакомить вас с новым видом цикла – for. И, на самом деле, он более подходит для реализации задачи с обратным отсчетом, как вы могли уже догадаться.
Отличие while от for
Сначала вопрос: как определить в каких случаях использовать while, а когда for? Если известно какое количество итераций цикла нужно совершить, то следует использовать for. Если количество заранее не известно или условие меняется по ходу сценария программы – тогда рекомендуется while (или do-while).
Нам точно известно, что количество итераций для программы отбратного отсчета равно количеству заданных секунд – в нашем случае это 5. И давайте реализуем это более корректно, используя цикл for. Его преимущества будут наглядно продемонстрированы на следующем примере.
for (i in 5 downTo 1) {
println("Реклама закончится через $i")
Thread.sleep(1000)
}
Пишем ключевое слово for. В круглых скобках будет условие (как и где нужно совершать перебор значений). Тело цикла расположено в фигурных скобках. Скопируем его из while с прошлого урока. В теле мы печатаем значение из списка или диапазона и ждем одну секунду. Теперь разберем новый синтаксис условия.
i – переменная
i – переменная, которая создается в рамках текущего цикла. Она будет служить контейнером для текущего элемента из предоставляемого списка, которое мы распечатываем. Название может быть произвольным. Как видите, мы уже не используем никаких дополнительных вспомогательных переменных вне счетчика (как это было в while).
downTo
5 downTo 1 – в этом месте условия записывается некая коллекция. Список, массив или диапазон чисел, который будет перебирать цикл. В данном случае с помощью downTo мы создали диапазон чисел (что является прогрессией) со значениями в порядке убывания. downTo означает, что значение переменной i в цикле for уменьшается на 1 на каждом шаге цикла. При подстановке диапазона в порядке возрастания, объявленного через точки (..) или until, значение i в цикле for будет увеличиваться на 1 на каждом шаге цикла.
Запускаем и видим корректно отрабатывающий обратный отсчет.
Шаг прохода
Помним, что шаг прохода (step) по умолчанию 1. Создадим еще один цикл для примера, подставим один из созданных выше диапазонов и укажем step 8.
for (i in range2 step 8) {
println(i)
}
Операторы перехода в Котлин
Отлично. Бывают ситуации, когда цикл не нужно отрабатывать до конца. Например, если мы нашли искомое значение. В этом случае продолжать перебор дальше бессмысленно, так как это пустая трата ресурсов. Тогда мы должны прервать цикл и выйти из него. Сделать это можно с помощью операторов перехода. Их будет три: break, continue и return.
Оператор break
Представим, что в нашем обратном отсчете показа рекламы есть кнопка “Пропустить”. И представим, что пользователь нажал на нее за 3 секунды до конца показа рекламы. Ни о каких перехватах нажатий сейчас не беспокоимся, просто воспринимаем это как факт. Задача сейчас состоит в том, чтобы за 3 секунды до конца отсчета закрыть рекламу и остановить счетчик. Напишем этот пример:
for (i in 5 downTo 1) {
if(i == 3) {
println("Пользователь нажад на кнопку \"Пропустить\"")
break
}
println("Реклама закончится через $i")
Thread.sleep(1000)
}
Что тут происходит. На каждой итерации цикла мы делаем проверку. Равно ли значение переменной 3. Если значение не равно, мы не заходим в условие if. Если значение равно, заходим в условие, печатаем информацию о том, что предполагаемый пользователь нажал на кнопку «Пропустить» и вызываем оператор break. Этот оператор завершит работу цикла полностью. Программа продолжить работать, если ниже после цикла будут дополнительные инструкции. Для наглядности добавим сообщение после цикла, это пригодится нам немного позже:
println("Продолжение работы программы вне цикла")
Экранирование символов
Кстати, вы можете обратить внимание на особое закавычивание слова “Пропустить”. Здесь мы применили так называемое экранирование символов. Экранирование – это замена в тексте управляющих символов на соответствующие текстовые подстановки. Я не могу написать несколько кавычек подряд. Это будет воспринято как окончание строки, что вызовет ошибку. Обратный слэш перед нужным символом превратит его в функциональный смысл в строчное значение.
Оператор continue
Теперь про оператор continue. При применении этого оператора мы можем прервать текущую итерацию. Это может понадобиться тогда, когда мы нашли искомое значение, что-то с ним сделали, и не хотим, чтобы исполнялись дальнейшие инструкции в теле цикла в текущей итерации. При этом работа цикла не заканчивается, а только лишь прерывается текущий круг. Для демонстрации модифицируем наш пример:
for (i in 5 downTo 1) {
if(i == 3) {
// println("Пользователь нажал на кнопку \"Пропустить\"")
// break
println("i == 3, i не будет распечатана")
continue
}
println("Реклама закончится через $i")
Thread.sleep(1000)
}
Вывод в консоль
Реклама закончится через 5
Реклама закончится через 4
i == 3, i не будет распечатана
Реклама закончится через 2
Реклама закончится через 1
В этом случае, когда значение i становится 3 – заходим в условие, печатаем информационное сообщение и вызываем continue. Цикл прерывается, не распечатав сообщение с рекламным счетчиком и начинается новый круг.
Оператор return
Наконец, оператор перехода return. В этом случае программа завершает выполнение функции в которой она находится. В нашем случае это метод main(). То есть в текущих условиях мы полностью остановим выполнение программы, так как у нас запущена одна единственная исполняемая функция main(). Еще раз модифицируем пример:
for (i in 5 downTo 1) {
if(i == 3) {
// println("Пользователь нажад на кнопку \"Пропустить\"")
// break
// println("i == 3, i не будет распечатана")
// continue
return
}
println("Реклама закончится через $i")
Thread.sleep(1000)
}
Вывод в консоль
Реклама закончится через 5
Реклама закончится через 4
В этом случае, как только значение i становится 3 – вызовется оператор return и программа завершит свое выполнение. Это можно заметить по отсутствию сообщения “Продолжение работы программы вне цикла”, которое мы печатали ниже по ходу программы.
Для тех, кто собрался стать Android-разработчиком
Пошаговая
схема
Описание процесса обучения от основ Kotlin до Android-разработчика
Бесплатные
уроки
Авторский бесплатный курс по основам языка программирования Kotlin
Обучающий
бот
Тренажер и самоучитель по Котлин – бесплатные тесты и практика