Итак, мы с вами рассмотрели более сложный цикл с ранним выходом. Так программисты называют те циклы, которые могут кончиться раньше срока, не выполнив заранее заданные количество итераций. Для того, чтобы выполнить ранний выход, Мы использовали оператор return, который прерывает выполнение функции, а вместе с ней и цикла. А существует ли способ прервать цикл, не прерывает функцию? Да, такой способ существует, он называется оператор break. Это слово так и переводится с английского - прервать. Когда данный оператор выполняется, цикл, в котором он находится, автоматически завершается. Функция, которую вы видите, предназначена для поиска так называемых совершенных чисел. Совершенные числа немного экзотическое понятие в математике, это число, равное сумме всех своих делить кроме себя. Совершенных чисел мало. В первые тысячи их всего три - 6, 28 и 496. В этом цикле мы суммируем все делители числа. Как только мы обнаруживаем, что сумма делителей превысила данное число, цикл прерывается оператором break. В этом же решении мы можем использовать оператор продолжения цикла continue. В отличие от break, он прерывает не весь цикл, а только одну его итерацию и переходит к следующей итерации, как бы продолжая цикл. В данном случае он позволил нам убрать лишнюю пару фигурных скобок, сделав код немного компактнее. Это почти всегда хорошо. Если число не делится на заданный делитель, остаток тела цикла пропускается. Другая важная возможность циклов, это возможность продолжить свою работу или завершиться исходя из заданного условия, а не по значению счетчика, как делает цикл for. Для этого существует два других цикла, а именно while, который называют циклом-предусловием и do...while, цикл с пост-условием. Рассмотрим эти два цикла на примере такой задачи: пусть нам дано число N и цифра M, и нам необходимо подсчитать сколько раз данная цифра входит в это число. Например, в число 8 цифра 1 входит ноль раз, а цифра 8 - один раз. В число девятнадцать цифра 9 входит один раз, в число 500, ноль входят два раза. Подсчитать цифры в числе очень просто. Мы всегда можем узнать младшую цифру в числе, взяв остаток от его деления на десять. Если же поделить число на десять, мы отбросим его младшую цифру и сможем перейти к следующей, поэтому нам достаточно последовательно делить число на десять и увеличивать счетчик цифр на один, если его младшая цифра соответствует данной. Решение с помощью цикла while перед вами. Программа проверяет условие продолжения цикла while перед началом каждой итерации, в том числе - самой первой. Если условие верно, итерация выполняется, если нет - цикл завершается. В данном случае цикл завершится, когда число после очередного деления станет равным нулю. Обратите внимание, что число выполненных итераций цикла в данном случае совпадает с числом всех цифр в числе. Конечно, это решение необходимо проверить тестами. В подобных случаях всегда важно проверять граничные случаи. Здесь - это число нуль, в которое входит одна цифра ноль. Хотя бы еще одно однозначное число, например, 7. Случай когда в число заданная цифра не входит ни разу, например, 21 с цифрой 3. И случай, когда заданная цифра является старшей в числе, например, 510 с цифрой 5. Добавим еще несколько примеров и запустим тесты. Видим, что тест завершился неудачей, причем именно на граничном случае. Действительно, если N равно нулю, то цикл не выполняется ни разу, и число цифр останется равным нулю. Это как раз тот случай, когда нам может помочь использование цикла do...while. Этот цикл очень похож на предыдущий, отличается он только тем, что условие продолжения цикла записывается в его конце. Проверяется оно тоже в конце, после окончания каждой интеграции цикла. Если условие истинно, мы переходим к следующей итерации, если же нет, цикл завершается. Обратите внимание, что цикл do...while, всегда выполняет хотя бы одну итерацию, и в данном случае, это нас спасает. Получив на входе число нуль, мы выполняем эту итерацию и обнаруживаем в нем одну цифру ноль. Теперь мы можем с чистой совестью запустить тест еще раз и убедиться, что они проходят, а значит задача решена нами верно. Рассмотрим теперь рекурсивные решения той же задачи. Вспомним, что у рекурсии должна быть база и переход. Какой случай может быть базовым в этой задаче? Очевидно тривиальный случай, когда число однозначное и либо совпадает с цифрой, либо нет. Для его реализации нам нужно проверить, что N меньше десяти и сравнить его с заданной цифрой. Теперь подумаем про переход. Перейти нам нужно от числа с большим количеством цифр к числу с меньшим количеством цифр. Проще всего, это сделать разбив заданное число на два. Одно состоящее только из младшей цифры получается из остатка от деления на десять, и другое - состоящее из остальных цифр, получается как результат от деления на десять. Вызвав для каждого из двух чисел рекурсивную функцию и сложив их результаты, получим искомый переход. Обратим внимание, что здесь рекурсивная функция вызывается на себя дважды. Может ли это привести к проблемам со скоростью работы, как это было с числами Фибоначчи? Нет, не может, поскольку один вызов всегда происходит для однозначного числа, а для него дальнейшей рекурсией не происходят. Число вызовов функции в ходе рекурсии в данном случае будет линейно зависеть от числа цифр в числе, например, для четырех цифр мы получим шесть вызовов, а для пяти - восемь вызовов. Можно вновь запустить тесты и убедиться, что никаких проблем не возникло. На этом мы заканчиваем теоретический материал данного урока. Пришло время порешать более сложные задачи. В качестве тренировки использования циклов while и do...while, я рекомендую вам задачу на поиск наименьшего общего кратного и задачу на расчет значения синуса, с помощью ряда Тейлора. Решив одну из них, вы закончите обязательный материал данного урока. Если вы чувствуете в себе силы, попробуйте решить одну из сложных задач третьего урока, например, задачу на поиск цифры с заданным номером в ряду цифр образованном числами Фибоначчи. Это последняя задача в уроке 3. Если у вас это получилось, вы можете быть уверены, что освоили материал урока про циклы достаточно хорошо, а мы с вами двигаемся дальше, к сложным структурам данных, которыми являются списки, массивы и строки.