Програмен език C++
(съдържание)

Част 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
a == b
!= 
неравно на
not equal to
a != b 
по-малко от
less than
a < b 
>
по-голямо от
greater than
a > b
<=
по-малко или равно на
less than or equal to
a <= b 
>=
по-голямо или равно на
greater than or equal to
a >= b
-------------- ------------------------ ----------------

 Например, ако 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
Ключови думи: клас , обект, обектно ориентирано програмиране , полиморфизъм