Java: Условия внутри тела цикла
Тело цикла, как и тело метода — это место выполнения инструкций. Значит, внутри него работает все изученное раньше, в том числе условные конструкции. Так программа повторяет одно действие много раз, но на каждом повторе принимает решение.
Цикл перебирает значения подряд, а условие внутри цикла решает, что делать с текущим значением. Рассмотрим метод, который считает, сколько раз буква входит в предложение:
App.countChars("Fear cuts deeper than swords.", 'e'); // 4
// Если ничего не нашли, то результат — 0 совпадений
App.countChars("Sansa", 'y'); // 0Сначала попробуйте ответить на вопросы:
- Является ли эта операция агрегацией?
- Какой будет проверка на вхождение символа?
А теперь посмотрим на код:
public static int countChars(String str, char ch) {
var i = 0;
var count = 0;
while (i < str.length()) {
if (str.charAt(i) == ch) {
// Считаем только подходящие символы
count = count + 1;
}
// Счетчик увеличивается в любом случае
i = i + 1;
}
return count;
}Это агрегирующая задача. Метод считает не все символы, но для подсчета суммы все равно приходится посмотреть каждый. Переменная count хранит результат и растет только тогда, когда текущий символ совпал с искомым.
Счетчик и условие отвечают за разное
В таком цикле удобно разделять две части. Счетчик i переводит программу к следующему символу, а if решает, что делать с текущим. Счетчик меняется на каждой итерации, а действие внутри if выполняется не всегда.
Это важно. Если увеличивать i только внутри if, цикл застрянет на первом неподходящем символе, потому что счетчик перестанет расти и условие i < str.length() навсегда останется истинным. Программа зациклится. Поэтому строка i = i + 1 стоит снаружи if и выполняется при любом исходе проверки.
Работа по шагам
Разберем вызов countChars("Sansa", 'a'). До цикла i равен 0, а count равен 0.
Шаг 1. Условие i < str.length() истинно. Символ с индексом 0 — это S. Он не равен a, блок if не выполняется. Затем i растет до 1.
Шаг 2. Условие снова истинно. Символ с индексом 1 — это a. Он совпал с искомым, поэтому count растет до 1. Затем i растет до 2.
Дальше цикл проверяет каждый символ. Подходящие он считает, остальные пропускает. Когда i станет равен длине строки, условие цикла станет ложным, и метод вернет накопленное значение count.
Условие внутри цикла может проверять что угодно — четность числа, совпадение символа, длину строки или значение переменной. Главное, чтобы счетчик продолжал меняться и цикл мог завершиться.
Задание
Метод из теории учитывает регистр букв. То есть A и a с его точки зрения разные символы. Реализуйте вариант этого же метода, так чтобы регистр букв был не важен:
App.countChars("HexlEt", 'e'); // 2
App.countChars("HexlEt", 'E'); // 2Character.toLowerCase()– переводит символ в нижний регистр
Java: Условия внутри тела цикла
Тело цикла, как и тело метода — это место выполнения инструкций. Значит, внутри него работает все изученное раньше, в том числе условные конструкции. Так программа повторяет одно действие много раз, но на каждом повторе принимает решение.
Цикл перебирает значения подряд, а условие внутри цикла решает, что делать с текущим значением. Рассмотрим метод, который считает, сколько раз буква входит в предложение:
App.countChars("Fear cuts deeper than swords.", 'e'); // 4
// Если ничего не нашли, то результат — 0 совпадений
App.countChars("Sansa", 'y'); // 0Сначала попробуйте ответить на вопросы:
- Является ли эта операция агрегацией?
- Какой будет проверка на вхождение символа?
А теперь посмотрим на код:
public static int countChars(String str, char ch) {
var i = 0;
var count = 0;
while (i < str.length()) {
if (str.charAt(i) == ch) {
// Считаем только подходящие символы
count = count + 1;
}
// Счетчик увеличивается в любом случае
i = i + 1;
}
return count;
}Это агрегирующая задача. Метод считает не все символы, но для подсчета суммы все равно приходится посмотреть каждый. Переменная count хранит результат и растет только тогда, когда текущий символ совпал с искомым.
Счетчик и условие отвечают за разное
В таком цикле удобно разделять две части. Счетчик i переводит программу к следующему символу, а if решает, что делать с текущим. Счетчик меняется на каждой итерации, а действие внутри if выполняется не всегда.
Это важно. Если увеличивать i только внутри if, цикл застрянет на первом неподходящем символе, потому что счетчик перестанет расти и условие i < str.length() навсегда останется истинным. Программа зациклится. Поэтому строка i = i + 1 стоит снаружи if и выполняется при любом исходе проверки.
Работа по шагам
Разберем вызов countChars("Sansa", 'a'). До цикла i равен 0, а count равен 0.
Шаг 1. Условие i < str.length() истинно. Символ с индексом 0 — это S. Он не равен a, блок if не выполняется. Затем i растет до 1.
Шаг 2. Условие снова истинно. Символ с индексом 1 — это a. Он совпал с искомым, поэтому count растет до 1. Затем i растет до 2.
Дальше цикл проверяет каждый символ. Подходящие он считает, остальные пропускает. Когда i станет равен длине строки, условие цикла станет ложным, и метод вернет накопленное значение count.
Условие внутри цикла может проверять что угодно — четность числа, совпадение символа, длину строки или значение переменной. Главное, чтобы счетчик продолжал меняться и цикл мог завершиться.
Задание
Метод из теории учитывает регистр букв. То есть A и a с его точки зрения разные символы. Реализуйте вариант этого же метода, так чтобы регистр букв был не важен:
App.countChars("HexlEt", 'e'); // 2
App.countChars("HexlEt", 'E'); // 2Character.toLowerCase()– переводит символ в нижний регистр
Ваше упражнение проверяется по этим тестам
import static org.assertj.core.api.Assertions.assertThat;
class AppTest {
public static void main(String[] args) {
assertThat(App.countChars("axe", 'a')).isEqualTo(1);
assertThat(App.countChars("", 'a')).isEqualTo(0);
assertThat(App.countChars("OpPa", 'p')).isEqualTo(2);
assertThat(App.countChars("OpPa", 'P')).isEqualTo(2);
}
}Решение учителя откроется через:
20:00
