Урок 6: «Циклы – это повторяющийся участок кода»
Это последний простой урок, который вы изучите в рамках данной рубрики. Дальше пойдут уже более сложные моменты.
Скажите, не хотелось бы вам хоть что-то указать компилятору, чтобы он автоматически делал сам? Вспомните наш список сотрудников. Мы вручную вводили каждый раз операторы:
cout<<”Введите ФИО сотрудника"<<endl; cin>>a[1];
Двадцать сотрудников и двадцать таких строчек. Опять же, сотрудников может быть и десять тысяч, тогда можно просто сойти с ума, набивая эта строчки. К счастью, С++ позволяет нам автоматизировать подобные действия. Для этого нужно использовать циклы.
Цикл – это участок кода, который будет повторяться до достижения условия выхода из цикла. Кстати, вместе с темой циклов я введу и понятие блок-схема.
Блок-схема – эта графическое отображение алгоритма вашей программы. Имела место в процедурном программировании, с приходом ООП практически потеряла актуальность. Тем не менее, некоторые аспекты удобно отображать именно с помощью блок-схемы.
Эти мои каракули (чертежник из меня никудышный) обозначают цикл с предусловием. Поясню, как работает этот цикл.
Программа доходит до ключевого слова while, обозначающего цикл и проверяет условие, указываемое в скобках. Если оно несовпадает с нужным нам, программа проигнорирует цикл. В другом случае она будет выполнять цикл до достижения нужного условия выхода из цикла. Запомните, цикл с предусловием может не выполниться ни разу! Это его главная особенность.
Объявляется цикл с предусловием так:
while (условие) { действия; }
Давайте теперь рассмотрим на примере, как он действует. Пусть нам нужно будет вывести список чисел, допустим до 20. Итак, наш цикл будет выглядеть таким образом:
intn=0; while(n>=20) { cout<<n<<endl; n++; }
Итак, что мы сделали. Вначале инициализировали переменную n нулем. Я мог дать ей единицу, суть не в этом (кстати, попробуйте присвоить n любое другое значение, в том числе и больше 20). Теперь для вывода списка я использовал цикл с предусловием, в котором указал, что цикл
будет выполняться до тех пор, пока n меньше или равен 20. Если переменная достигает 21, то цикл заканчивает свою работу. Ну а в цикле я выводил на консоль значение n, а потом прибавлял ее значение инкрементом (++).
Данный цикл применяется только тогда, когла мы точно не знаем, будет ли выполняться цикл вообще. Я мог присвоить n значение 100 и цикл бы не сработал. Это очень удобно. Компилятор встретил цикл, проверил условие и понял, что ему дальше нужно делать.
В противовес этому цикл, существует цикл с постусловием. Его отличительной чертой является тот факт, что данный цикл выполнится хотя бы один раз, пускай даже, если условие и не будет соответствовать нужному. В итоге это замедляет работу программы. Объявляется цикл с постусловием так:
do { действия; } while(условие)
Ну а графически это будет выглядеть так:
Как видите, сначала действия выполнются, а уже потом проверяется условие. Чтобы не запутаться, скажу вам сразу – цикл с постусловием следует использовать только тогда, когда необходимо, чтобы цикл сработал хотя бы один раз. В остальных случаях следует использовать цикл с предусловием или цикл с параметром. Давайте нашу задачу реализуем с помощью цикла с постусловием:
int n=0; do { cout<<n<<endl; n++; } while(n>=20)
Если вы читали урок внимательно, то должны понять разницу между этими двумя циклами. Ради эксперимента вновь поменяйте значение n на больше 20.
Условием цикла может быть и логическое значение:
bool flag=true; while(flag){ действия; }
или же
while(!flag) { действия; }
Обратите внимание, что в этом случае будет выполняться бесконечный цикл (цикл, который не может найти условие для выхода). В этом случае его нужно будет явно прервать, например присвоив в первом случае переменной flag значение false:
bool flag=true; int n=1,k=5; while (flag) { if(++n>--) flag=false; }
Теперь что за восклицательный знак я поставил перед flag? Это отрицание. Если логическое true, то в следствие знака ! оно будет false. Думаю, что с этим понятно. Просто запомните, что если вдруг вам захотелось проверить условие способом if (flag!=true), то лучше использовать if (!flag).
Думаю, что с этими двумя циклами у вас проблем возникнуть не должно. На практике я знаю, что новички сыплются на следующем виде циклов – цикле с параметром.
Что же это за цикл? В предыдущих циклах мы не знали, сколько раз выполнится цикл. Мы ставили условие циклу и компилятор гонял этот цикл. Цикл мог выполниться миллион раз, а мог и не разу.
В отличие от низ, цикл с параметром четко знает, сколько раз он должен выполниться. Ведь мы сами указываем в параметре это значение. Объявляется данный цикл таким образом:
for (нижняя граница цикла; верхняя граница цикла; шаг приращения/ уменьшения)
{
действия
}
На нашем, компьютерном языке C++ он будет записан следующим образом. Кстати, запомните важную вещь – благодаря различным уровням видимости переменных в С (мы об этом поговорим позже), рекомендуется нижнюю границу цикла объявлять в параметре. Кроме того, считается признаком хорошего программирования называть переменную цикла именем I (от iterator). В качестве примера возьмем уже наш многострадальный список сотрудников, который мы рассматривали в прошлом уроке. К слову могу сказать, что для массив и цикл с параметром – это хороший союз. Фактически, этот цикл применяется, в основном, для обхода массива. Хотя им можно решать и другие задачи. Но просто в массиве он очень удобен:
#pragma warning (disable:4786)//отключим предупреждение #include "stdafx.h" #include <iostream>; //содержит объекты ввода-вывода #include <locale.h>; //настройки языка и времени #include <string>; using namespace std; int_tmain(int argc,_TCHAR*argv[]) { setlocale(0,"");//установим язык по-умолчанию (русский) constintsize=20;//размер массива stringsotr[size]; for(int i=0;i<size;i++) { cout<<"Введите имя сотрудника"<<endl; getline(cin,sotr[i]); } system("pause");//чтобы окно не закрылось return0; }
Скомпилируйте и запустите. У вас должно получиться нечто вроде:
Теперь давайте пробежимся по коду. Вначале директивой #pragma warning (disable:4786) я отключил предупреждения компилятора. В принципе, вы его можете оставить, вот только при компиляции будут доставать «возможные ошибки» (так уж устроен этот компилятор). Важно очень подключить заголовочный файл <string>, в котором находится нужная нам функция getline. Внимание, ввести строку типа string в C++ можно только этой функцией. Объект cin, которым мы раньше пользовались, здесь бесполезен.
Обратите внимание на конструкцию for. Это и есть наш цикл с параметром. Мы объявили итератор, указали, что нижняя граница цикла равна 0 ( а можем любое число использовать, в зависимости от задачи), верхней границе указали, что она меньше константы size (т.к. отчет с 0 для массива, вы же помните?), ну и шаг массива указали равным 1 (использовали для этого инкремент). О функции getline я уже рассказывал.
Попробуйте, модифицируйте программу таким способом, чтобы она выводила список записанных сотрудников из уже введенного массива. Для этого используйте еще один цикл for. Только уже будете выводить таким образом:
cout<<sort[i]<<endl;
Думаю, что вы обратили внимание на то обстоятельство, что вместо индекса массива я использовал значение итератора i. Это тоже самое, как если бы я писан не текущее i, а явно указывал sort [1] и т.д. В этом одно из достоинств массива. К слову, и в двух других циклах вы вольны ввести итератор и сами его приращивать в цикле, давая его значение своему массиву, это хотя и не рекомендуется, но не возбраняется.
Блок-схему рисовать, я считаю, бессмысленно, так как код нагляден. Вместо этого давайте еще немного расскажу о цикле for.
C допускает делать с ним различные безобразия. Вот, например, один из способов вывода ряда фибоначчи:
for (i=0, j=1, k=0, fib =0; i<50; i++, fib=j+k, j=k, k=fib)
Причем, здесь вычисление приводится в самом параметре. Хотя этот способ может показать о вашей крутизне, он гораздо хуже читается. Но знайте, что так можно поступать.
Кроме этого стоит поговорить о шаге приращения. Его можно задать произвольно. Допустим, нам нужен каждый второй элемент нашего массива. Тогда мы укажем не i++, а i+=2. И так по аналогии. Кроме того, часто нужен будет делать цикл на уменьшение. Тогда нижней границей будет ваше верхнее число, верхней – нижнее, а использовать нужно будет декремент (i—). В алгоритмических приемах вы сами вскоре все увидите.
Как видите, циклы очень удобная штука. Однако в циклы еще включены некоторые управляющие операторы, делающие их гораздо удобнее. Особенно это помогает при использовании вложенных циклов. Да-да, как и любая другая конструкция, циклы могут быть вложенными друг в друга:
while(true) { for(int i=0;i<100;i++) { ;} }
При реализации многих алгоритмов этот подход очень важен. Но не менее важно прервать выполнение цикла по какому-либо условию. В C и C++ это позволяют сделать операторы continue и break. При встрече со словом continue, компилятор тут же бросает выполнение всей текущей итерации цикла и заходит на новую итерацию. Цикл все равно будет продолжен.
А вот с break ситуация несколько иная – встретив это слово, C++ прервет работу цикла и выйдет из него. Ему будет абсолютно все равно, что после этого слова стоит еще целый блок действий. Мы указали C++, что хотим прервать цикл по нашему условию.
Давайте продемонстрируем работу break в примере, когда нам нужно будет осуществить линейный (последовательный) поиск в массиве. Хотя и обещал рассмотреть алгоритмы в специальном уроке, но программирование на то и программирование. Без алгоритмов никуда. Линейный поиск – это последовательный перебор элементов с сравнением каждого из них с поисковым запросом. Считается одним из самых медленных алгоритмов. Однако при работе с неупорядоченным массивом (не отсортированным), линейный поиск дает максимальный результат. А так как у нас этот массив некому сортировать, то последовательный поиск – то, что доктор прописал:
for(int i=0;i<size;i++) { if(sotr[i]="Иванов") { cout<<"Искомая личность найдена"<<endl; break; } } }
Мы нашли в массиве нашу фамилию (точнее, ее первое вхождение). Дальнейшее выполнение перебора в нашем варианте бессмысленно (мы не учитываем, что могут быть однофамильцы). Поэтому сразу же прерываем цикл. Если условие ни разу не выполнилось, цикл проработает до конца. Думаю, что с этим понятно.
Поэкспериментируйте с оператором continue. Придумайте задачку и реализуйте ее сами. На этом урок о циклах я заканчиваю.