Част 6. Цикли (Loops)
В тази лекция ще разгледаме трите оператора за цикли в C++, както и някои помощни понятия като логическите стойности истина (True) и лъжа (False). Циклите позволяват повторното изпълнение на група оператори в програма съобразно желанието на потребителя - т.е. те правят възможно взаимодействието на потребителя с програмата. Без тях и без операторите за управление (decisions) програмата ще се изпълнява само по един и същи начин, което я лишава от функционалност.
1. Булеви стойности (истински и лъжливи стойности, True and False Values). Циклите, както и управляващите оператори (decisions, if-then-else и switch), които ще разгледаме в следващите лекции, вземат решение коя група оператори ще бъде изпълнена въз основа на стойността на една променлива. Стойността на тази променлива може да бъде истина (True) или лъжа (False). В някои други програмни езици променливата, която има две стойности истина или лъжа се нарича булева (Boolean или Bool), като даже и в C++ има вграден булев тип - bool (built-in Boolean type).
Въпреки това, почти всяка една променлива в C++ може да изпълнява ролята на булева променлива. Например, нека a е дефинирано като
int a;
тогава ако a има стойност, различна от нула (напр. a = -6) то тя се приема за истина (True). Ако a = 0, то тогава тя играе ролята на лъжа (False). Същото е и с променливи от другите целочислени типове (char, int, short, long), както и с типовете плаваща запетая (float и double): ако те имат стойност, различна от нула, те изпълняват ролята на истина (True), и обратно ако са равни на нула - ролята на лъжа (False).
Много често вместо променлива, чиято стойност управлява цикъла, стои някакъв логически израз или израз, който сравнява някакви стойности. Например изразът
a >= b
сравнява стойностите на a и b, и ако a е по-голямо или равно на b, то този израз има стойност истина (True). И обратно, ако b е по-голямо от a, то изразът има стойност лъжа (False).
В таблица 1 са дадени така наречените оператори за сравняване (relational operators).
Таблица 1. Операторите за сравняване.
Символ Symbol -------------- |
Значение Meaning ------------------------ |
Пример Example ---------------- |
|
equal to |
|
|
not equal to |
|
|
less than |
|
|
greater than |
|
|
less than or equal to |
|
|
greater than or equal to |
|
-------------- | ------------------------ | ---------------- |
Например, ако a=2 и b=4, то
a == b ще
е лъжа
(False),
a != b ще
е истина
(True),
a < b ще
е истина
(True),
a <= b ще
е истина
(True),
a > b ще
е лъжа
(False),
a >= b ще
е лъжа
(False).
2. Оператор за цикъл while (while loop). Той има вида while ( <condition> ) <statement>, където групата оператори <statement> се изпълняват непрекъснато, докато условието <condition> е истина (True). Ние на български нямаме две "докато", както в английския - while и until, затова предното изречение да се разбира, че при стойност истина на израза <condition>, групата оператори <statement> се изпълняват, т.е. ако условието <condition> стане лъжа (False), то се изпълнява операторът, следващ цикъла, а не <statement>.
Например цикълът
while(ch
!= 'е') // no ; after )
{
cout << "Enter a character: ";
cin >> ch;
}
// no ; after }
ще се изпълнява докато потребителят натисне буквата e (и клавиша <Enter>), т.е тогава ще спре изпълнението на цикъла: условието на цикъла, ch != 'е', е истина, ако символът ch е различен от e.
Както се вижда от този пример, операторът while е следван от условието (conditional expression или само condition), което е заградено в скоби. След него няма точка и запетая. Слагането на точка и запетая след скобите, както в
while(ch
!= 'е'); // this
is a nonsence construction
{
cout << "Enter a character: ";
cin >> ch;
}
е една от най-неприятните и трудно откриваеми грешки, която начинаещите правят. В този случай, цикълът ще се изпълнява безкраен брой пъти, ако стойността на символната променлива ch е различна от 'е'. Ако тя има стойност 'е', то цикълът не се изпълнява, а се изпълняват двата оператора, заградени с големите скоби, и се преминава към оператора след тях, а не отново към началото на цикъла.
Както се разбира от анализа на грешката по-горе, за да се изпълни поне веднъж (правилният или неправилният while оператор), то трябва предварителноch да има стойност, различна от 'е'. Иначе, групата оператори, заградени с големите скоби, няма да се изпълнят.
Обърнете внимание, също, че след затварящата голяма скоба няма точка и запетая. Самите големи скоби служат за разделител и не е необходима точка и запетая след тях. Скобите служат да обединят няколко оператора в един, който да бъде изпълнен, ако условието е истина. Ако имаме само един оператор, скобите не са необходими - например изразите по-долу ще разпечатат числата от 0 до 9.
int i = 0;
while (i < 10)
cout << i++ << endl;
В заключение ще отбележим, че цикълът може въобще да не се изпълни, например ако още при достигането му, условието в скобите е неистина. И точно това е една от най-желаните характеристики на този цикъл - способността му да не се изпълняват операторите в тялото му при определено условие.
3. Оператор за цикъл do-while (do-while loop). Той има вида do <statement> while ( <condition> );, където групата оператори <statement> се изпълняват непрекъснато, докато условието <condition> стане лъжа (False). Както споменахме, ние на български нямаме две "докато", както в английския - while и until, затова предното изречение да се разбира, че при стойност истина на израза <condition>, групата оператори <statement> се изпълняват, т.е. ако условието <condition> стане лъжа (False), то се изпълнява операторът, следващ цикъла, а не <statement>.
Следният пример
int x = 1, y;
do
// there is NO ;
{
cout << "\nEnter two numbers (to quit, set first to 0): ";
cin >> x >> y;
cout << "Their product is " << x * y;
}
while (x != 0);
// there is ;
ще изчислява произведението на две числа, докато първото е различно от нула. За да прекратим изпълнението на този цикъл трябва да въведем x, равно на нула.
След doняма точка и запетая, но накрая след while (<condition>) има! Условието задължително е в кръгли скоби. Въпреки, че често този цикъл се нарича само do цикъл, то частта while(<condition>) е задължителна.
Цикълът do-while се разглежда от професионалните програмисти като излишна и тромава структура, която винаги може да се замени с цикъла while. Но понякога изпълнението поне веднъж на операторите в цикъла е задължително и тогава цикълът do-while има своето място в програмата.
4. Оператор за цикъл for (for loop). Той има вида for ([<initialization>]; [<condition>]; [<increment>]) <statement>, където групата оператори <statement> се изпълняват непрекъснато, докато условието <condition> стане лъжа (False). Както споменахме, ние на български нямаме две "докато", както в английския - while и until, затова предното изречение да се разбира, че при стойност истина на израза <condition>, групата оператори <statement> се изпълняват, т.е. ако условието <condition> стане лъжа (False), то се изпълнява операторът, следващ цикъла, а не <statement>.
Следният пример изкарва числата от 0 до 9 на екрана
int j; // define the loop variable
for(j
= 0; j < 10; ++j) // cycle 10 times
cout << j << endl; // print j
j = 0 се нарича инициализиращо условие (initialization expression) - това е <initialization> в горната дефиниция. j < 10 е условието, което се проверява (test expression) - ако то е истина, цикълът се изпълнява. И последно, ++j е изразът за промяна на променливата (increment expression), който израз в примера увеличава стойността на j с единица.
При достигане на цикъла for, програмата първо инициализира променливите в инициализиращото условие. В примера по-горе изпълнява j = 0. След това проверява тестващото условие. В примера по-горе се проверява дали j < 10. Ако то е истина, се изпълняват операторите в цикъла (той е само един - cout << j << endl), и чак след това се изпълняват операторите в израза (изразите) за промяна на променливата (променливите): в примера той също е само един - ++j. След това програмата отново проверява тестващото условие (изпуска инициализиращото условие!), и ако тестващото условие е истина, изпълнява операторите в цикъла и отново променя променливата (променливите). Чак при тестващо условие, което дава неистина, се изпълнява първият оператор след цикъла.
В дадения по-горе пример <statement> се състои само от един оператор - cout << j << endl;. При наличие на няколко оператора, те се заграждат с големи скоби и след затварящата скоба не се пише точка и запетая. Например следващият програмен фрагмент изкарва на екрана числата от 0 до 9 и тяхната сума.
int j, sum = 0; // define the loop variable
for(j
= 0; j < 10; ++j) // cycle 10 times
{
sum += j;
cout << "j = " << j << " sum = " << sum <<
endl; // print j
}
Може да има няколко инициализиращи израза и няколко нарастващи израза. Те се разделят със запетайки. Например, горният фрагмент може да се напише по следния сбит начин:
int j, sum; // define the loop variable
for(j
= 0, sum = 0; j < 10; ++j, sum += j)
// cycle 10 times
cout << "j = " << j << " sum = " << sum <<
endl; // print j
Тук инициализиращото условие е от един оператор с две дефиниции и иницализации, едновременно - int j = 0, sum = 0, а нарастващото условие е от два оператора - ++j и sum += j. Операторът sum += j означава sum = sum + j и с него ще се запознаем в някоя от следващите лекции.
В дефиницията по-горе и трите израза (initialization, test and increment expression) са заградени със средни скоби, което показва, че не са задължителни, т.е. напълно е възможно да се напише следният цикъл:
for(;
; )
{
оператори
}
но за да не стане безкраен цикъл трябва в тялото му, между големите скоби, да има оператор, с който се излиза от цикъла. Това обикновено е операторът goto, чийто синтаксис е goto <identifier>;, но той прави програмния код уплетен като спагети (spaghetti like) и затова тази структура не се препоръчва.
А ето как би изглеждала главната функция на тази странна конструкция (с празен for оператор):
void
main(int argc, char* argv[] )
{
int j = 0;
for(; ; ) // cycle 10 times
{
if (j >= 10) goto aa;
++j;
cout << j << endl; // print j
}
aa: cout << "\nEnd of the program!";
}
// main()
5. Вмъкнати цикли (Nested Loops). В програмата може да имате колкото си искате вмъкнати един в друг цикли. Обикновено два вмъкнати цикъла се използват за операции с елементите на правоъгълна матрица, но засега не сме учили масиви (arrays), за да се запознаем с такъв пример. Но ето един пример, който чертае триъгълник от звездички на екрана:
for(int i = 0; i < 20; i+=2)
// cycle 10 times
{
for(int j = 0; j < (20 - i)/2; ++j) // cycle (20 - i)/2 times
cout << ' ';
// print ' '
for(int j = i/2; j <= i; ++j)
// cycle i/2 + 1 times
cout << '*';
// print '*'
for(int j = i/2; j < i; ++j)
// cycle i/2 - 1 times or 0 times if (i/2 - 1 < 1)
cout << '*';
// print '*'
cout << endl;
// print new line
}
За упражнение е достатъчно да се копират в програмния редактор примерите, които бяха дадени в материала.
Литература
[1] Robert Lafore; C++ Interactive Course. Waite Group Press, Macmillan Computer Publishing, 1996.
Автор: Проф. Процесор Аритметиков
[това е материал от брой 14 от декември 2007 г. на списание "Коснос" www.kosnos.com]
Keywords: С++,
OOP programming , C++ , Classes , Inheritance , Reusability , Creating
New Data Types , Polymorphism and Overloading
Ключови думи: клас
, обект, обектно ориентирано програмиране , полиморфизъм