9. Управляващи оператори

План:
Програмиране на алтернативи
Вложени условни оператори
Булеви операции
Отложени пресмятания
Закони на де Морган
Оператор за цикъл for
Оператор за цикъл do/while
Четене на данни и пренасочване на входния и изходния потоци
Симулации


** Програмиране на алтернативи (избор на един от няколко варианта).
Пример: Да се намери стойността (в долари) на купчина американски монети (1 penny = 1 цент, 1 nickel = 5 цента, 1 dime = 10 цента, 1 quarter = 25 цента). Купчината съдържа еднакви монети.

// coins5.cpp
#include
<iostream>  #include <string>  using namespace std;  int main()  {  cout << "Enter coin name: ";     string name;     cin >> name;     double value = 0;     if (name == "penny") value = 0.01;     else if (name == "nickel") value = 0.05;     else if (name == "dime") value = 0.10;     else if (name == "quarter") value = 0.25;     else        cout << name << " is not a valid coin name\n";
    cout << "Value = " << value << "\n";
    return 0
В тази задача редът на алтернативите не е важен.
В някои случаи редът на алтернативите е важен.
Пример: Програма, която показва описание на вероятното въздействие на земетресение въз основа на магнитуда му по скалата на Рихтер.
// richter.cpp
#include
<iostream> #include <string> using namespace std; int main() {  cout << "Enter a magnitude on the Richter scale: ";    double richter;    cin >> richter;    if (richter >= 8.0)       cout << "Most structures fall\n";    else if (richter >= 7.0)       cout << "Many buildings destroyed\n";    else if (richter >= 6.0)       cout << "Many buildings considerably damaged, "          << "some collapse\n";    else if (richter >= 4.5)       cout << "Damage to poorly constructed buildings\n";    else if (richter >= 3.5)       cout << "Felt by many people, no destruction\n";    else if (richter >= 0)       cout << "Generally not felt by people\n";    else       cout << "Negative numbers are not valid\n";    return 0; }

** Вложени условни оператори.
Пример: Задача за определяне на годишен данък в САЩ.
Ако сте несемеен
Ако облагаемата сума е над но не повече от данъкът е върху сумата над
0 21400 15% 0
21450 51900 3217.50 + 28% 21450
51900 - 11743.50 + 31% 51900
Ако сте семеен
Ако облагаемата сума е над но не повече от данъкът е върху сумата над
0 35800 15% 0
35800 86500 5370.00 + 28% 35800
86500 - 19566.00 + 31% 86500


// tax.cpp
#include
<iostream>
#include <string>
using namespace std;

int main()
{  const double SINGLE_LEVEL1 = 21450.00;
   const double SINGLE_LEVEL2 = 51900.00;

   const double SINGLE_TAX1 = 3217.50;
   const double SINGLE_TAX2 = 11743.50;

   const double MARRIED_LEVEL1 = 35800.00;
   const double MARRIED_LEVEL2 = 86500.00;

   const double MARRIED_TAX1 = 5370.00;
   const double MARRIED_TAX2 = 19566.00;

   const double RATE1 = 0.15;
   const double RATE2 = 0.28;
   const double RATE3 = 0.31;

   double income;
   cout << "Please enter your income: ";
   cin >> income;

   cout << "Please enter s for single, m for married: ";
   string marital_status;
   cin >> marital_status;

double tax;
   if (marital_status == "s")
   {  if (income <= SINGLE_LEVEL1)
         tax =  RATE1 * income;
      else if (income <= SINGLE_LEVEL2)
         tax = SINGLE_TAX1 + RATE2*(income - SINGLE_LEVEL1);
      else
         tax = SINGLE_TAX2 + RATE3*(income - SINGLE_LEVEL2);
   }
   else
   {  if (income <= MARRIED_LEVEL1)
         tax =  RATE1 * income;
      else if (income <= MARRIED_LEVEL2)
         tax = MARRIED_TAX1 + RATE2*(income - MARRIED_LEVEL1);
      else
         tax = MARRIED_TAX2 + RATE3*(income - MARRIED_LEVEL2);
   }
   cout << "The tax is $" << tax << "\n";
   return 0;
}

** Булеви (логически) операции
Логически константи - възможни са само две стойности:
истина да true 1 изпълнено вярно удовлетворено
лъжа не false 0 нарушено невярно неудовлетворено

Операция конюнкция (и) има стойност истина, ако и двата аргумента са истина.
Пример: Проверка за час и минути.
if (now.get_hours() == homework.get_hours() &&
now.get_minutes() == homework.get_minutes())
cout << "The homework is due right now!\n";
Операция дизюнкция (или) има стойност истина, ако и поне един от двата аргумента е истина.
Пример: Цена на колет от континенталната част на САЩ до Аляска или Хавай.
if (state == "HI" || state == "AK")
shipping_charge = 10.00;
Основни логически операции:
и конюнкция and && бинарна операция
или дизюнкция or || бинарна операция
не отрицание not ! унарна операция

Таблици за стойностите на логическите операции:

A
B
A && B
true
true
true
true
false
false
false
Any
false

A
B
A ||B
true
Any
true
false
true
true
false
false
false

A
!A
true
false
false
true


** Отложени пресмятания (lazy evaluations)

Затова и двата случая не се проверява стойността на втория аргумент - нарича се отложено пресмятане.

Пример. Приложение на отложени пресмятания - ако има грешка на входа, стойността на area е неопределена и няма смисъл да се проверява дали е положителна.

cin >> area;  
if (cin.fail() || area < 0) cout << "Input error.\n";
При проверката и грешка във входния поток (първият аргумент на операция or е true) не се проверява условието area < 0.
** Закони на DeMorgan  [Аугуст де Морган (1806-1871)].
Пример: Изпращане на колет от САЩ извън континенталната част, т.е. друга държава или щатите Аляска и Хавай.
if (!(country == "USA" && state != "AK" && state != "HI"))
shipping_charge = 20.00;
Законите на DeMorgan може да се използват за да се опростят тези булеви изрази.
not (A and B)
е също като
not A or not B
not (A or B)
е също като
not A and not B
Проверка с изчерпване на всички случаи - булевите стойности на A и B.
Преобразуване:
state != "AK" && state != "HI"
като
!(state == "AK") && !(state == "HI")
!(state == "AK") && !(state == "HI")
DeMorgan
!(state == "AK" || state == "HI")
!(country == "USA" && 
!(state == "AK" || state == "HI"))
DeMorgan
!(country == "USA") || 
(state == "AK" || state == "HI")
и окончателно
country != "USA" || state == "AK" || state == "HI"
с приложение на отложени пресмятания.

** Оператор за цикъл for
Най-често използваната форма на цикъл е:
i = start;
while (i <= end)
{ . . .
i++;
}
За тази форма има специален оператор за цикъл:
for (i = start; i <= end; i++)
Пример: Пресмятане на функцията факториел: n! = 1.2.3.4...n
// forfac.cpp
#include
<iostream>
using namespace std;

long forfac(int n)
{  long product = 1;
   for (int i = 1; i <= n; i++) product = product * i;
return product;
}

int main()
{  cout << "Please enter a number: ";
   int n;
   cin >> n;
   cout << n << "! = " << forfac
(n) << "\n";
   return 0;
}
Цикъл с намаляване на променливата на цикъла.
for (int n = 10; n >= 0; n--) ...
Нарастването или намаляването на стъпката на цикъла може да не е 1.
for (x = -10; x <= 10; x = x + 0.5) ...
Възможно е да се сложат несвързани условия в цикъла.
for (rate = 6; month--; cout << balance) ... // BAD
Безкраен цикъл.
for (;;) ...
while(true) ..

** Оператор за цикъл do/while.
Понякога се налага да се изпълни тялото на цикъла (операторите в  тялото на цикъла) и след това да се провери теста за край на цикъл.
do
{ statements
}
while (condition);
Пример: Древните гърци са използвали прост алгоритъм за пресмятане на корен квадратен.
Редицата
  x0 = axn+1 = ( xn + a/xn ) / 2, n = 0,1, 2, 3, ...
е сходяща и има граница корен квадратен от a.

do
{ xold = xnew;
xnew = (xold + a / xold) / 2;
}
while (fabs(xnew - xold) > EPSILON);

// sqroot.cpp
#include
<iostream> #include <cmath> using namespace std; int main() {  cout << "Please enter a number: ";    double a;    cin >> a;        const double EPSILON = 1E-14;    double xnew = a;    double xold;    do    {  xold = xnew;       xnew = (xold + a / xold) / 2;    }    while (fabs(xnew - xold) > EPSILON);    cout << "The square root is " << xnew << "\n";    return 0; }

** Вложени цикли
Пример: Таблица от стойности с крайна сума за вложени на влог пари с дадена годишна лихва и брой години. 
Таблицата показва съдбата на 10 хиляди щатски долара, инвестирани с различни лихвени проценти за различен брой години.
Псевдокод:
print table header
double rate;
for (rate = RATE_MIN; rate <= RATE_MAX;
rate = rate + RATE_INCR)
{ print table row
}
Хедърът на таблицата също се получава е един цикъл:
cout << "Rate     ";
int year;
for (year = YEAR_MIN; year <= YEAR_MAX;
year = year + YEAR_INCR)
{ cout << setw(2) << year << " years";
}
За отпечатване на един ред се използва още един цикъл - така получаваме вложен цикъл.
// table.cpp
#include
<iostream> #include <iomanip> #include <cmath>
using namespace std;

int main()
{  const double RATE_MIN = 5;
   const double RATE_MAX = 10;
   const double RATE_INCR = 0.5;
   const int YEAR_MIN = 5;
   const int YEAR_MAX = 30;
   const int YEAR_INCR = 5;

   /* print table header */

   cout << " Rate  ";
   int year;
   for (year = YEAR_MIN; year <= YEAR_MAX; 
        year = year + YEAR_INCR)
      cout << setw(2) << year << " years  ";
   cout << "\n";
   cout << fixed << setprecision(2);

   double rate;
   double initial_balance = 10000;
   for (rate = RATE_MIN; rate <= RATE_MAX; 
        rate = rate + RATE_INCR)
   {  
      /* print table row */
       int year;
       cout << setw(5) << rate;
       for (year = YEAR_MIN; year <= YEAR_MAX;
          year = year + YEAR_INCR)
       {  double balance = 
             initial_balance * pow(1 + rate/100, year);
          cout << setw(10) << balance;
       }
       cout << "\n";
   }
   return 0;
}
В някои случаи броят на итерациите (изпълнения на тялото на цикъла) на вътрешния цикъл зависи от броячът на итерациите на външния цикъл.
Пример:  Какво ще отпечати следващата програма?

// triangle.cpp
#include <iostream>
using namespace std;

int main()
{  cout << "Enter number of rows: ";
   int n;
   cin >> n;

   for (int i = 1; i <= n; i++)
   {  for (int j = 1; j <= i; j++) cout << "[]";
      cout << "\n"; 
   }
   return 0;
}

[][][]
[][]
[]


** Четене на данни и пренасочване на входния и изходния потоци
Конструкцията 
while (cin >> x) чете данни от входа докато не се появи грешка във входния поток.
Грешка във входния поток се появява когато:
-- има несъответствие между типа на променливата x и типа на въведената стойност или
-- когато се въведе специален символ за край на входа (край на файла).
За операционната система Windows този символ е Ctrl-Z (ASCII 26),  за UNIX и MAC OS - Ctrl D (ASCII 04).

Пример: Брой на думите от входния поток.
// words.cpp
#include
<iostream> #include <string> using namespace std; int main() {  int count = 0;    string word;    while (cin >> word) count++;    cout << count << " words." << endl;    return 0; }
* Пренасочване на входа (cin)
    C:\my\>myprog < a_cin.txt
Вместо от клавиатурата, операционната система пренасочва входа от текстов файл, в случая това е файла 
a_cin.txt.
По този начин програмата за намиране на броя на думите може намери броя на думите в текстов файл (едно по-смислено приложение на тази програма).
* Пренасочване на изхода (cout)
    C:\my\>myprog > a_cout.txt 
Вместо на екрана, операционната система пренасочва изхода на текстов файл, в случая това е файла a_cout.txt.

* Четене на редове
Текстови файлове в различни операционни системи - край на ред:
- Unix - LF (Line feed, '\n', 0x0A, 10 in decimal)
- MAC OS - CR (Carriage return, '\r', 0x0D, 13 in decimal)
- Windows - LF CR
Константа endl от потоковата библиотека за извеждане на "край на ред" по стандарта на съответната ОС.
За четене на редове се използва функцията getline.
string line;
while (getline(cin, line))
{ process line
}
Пример: Брой на редовете във входния поток.
// lines.cpp
#include <iostream>
#include <string>
using namespace std;

int main()
{  int count = 0;
   string lines;
   while (getline(cin, lines)) count++;
   cout << count << " lines." << endl;
   return 0;
}

* Четене на символи - букви, цифри и специални символи
За въвеждане на символ по символ (включително малки ASCII кодове) се използва член-функцията get:
char ch;
while (cin.get(ch))
{ process ch
}
Пример: Брой на символите (знаците) във входния поток.
// chars.cpp
#include <iostream>
using namespace std;

int main()
{  int count = 0;
   char ch;
   while (cin.get(ch)) count++;
   cout << count << " chars." << endl;
   return 0;
}

** Симулации
Симулация е решаване на задача с генериране на случайни събития и оценката на техните резултати.
C++ библиотеката разполага с генератор на случайни числа, който произвежда числа, чието поведение прилича на случайно.
Функцията rand() връща случайно число между 0 и RAND_MAX (обикновено 32 767).
Тези числа действителните произхождат от много дълга редица от числа, изчислени от доста прости формули; те просто се държат като случайни числа.
Поради тази причина те често се наричат псевдослучайни числа.
Пример: Следващатата програма произвежда точно същия изход всеки път, когато програмата се изпълнява (защото числата са получени с формула).
int main()
{ int i;
for (i = 1; i <= 10; i++)
{
int r = rand();
cout << r << "\n";
}
return 0;
}
random.cpp

Когато се зададе нова стойност на началото на редицата (променлива seed), ще се получи и друга редица от случайни числа.
За тази цел има функция srand(seed), която променя началото на редицата.
Когато се иска при различни пускания на програмата да се генерират различни числови редици, за параметър на функцията srand се използва времето от компютърния часовник.
Time now;
int seed = now.seconds_from(Time(0,0,0));
srand(seed);
За генериране на случайни цели числа в интервала  [a, b] се използва следната функция:
int rand_int(int a, int b)
{
return a + rand() % (b - a + 1);
}
Пример: Хвърляне на зарове.
// dice.cpp 
#include
<iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; /**    Sets the seed of the random number generator. */ void rand_seed() {  int seed = static_cast<int>(time(0));    srand(seed); } /**     Compute a random integer in a range    @param a the bottom of the range    @param b the top of the range    @return a random integer x, a <= x and x <= b */ int rand_int(int a, int b) {  return a + rand() % (b - a + 1); } int main() {  rand_seed();    int i;    for (i = 1; i <= 10; i++)    {  int d1 = rand_int(1, 6);       int d2 = rand_int(1, 6);       cout << d1 << " " << d2 << "\n";    }    cout << "\n";    return 0; }
За генериране на случайни числа тип double в интервала  [a, b]:
double rand_double(double a, double b)
{
return a + (b - a) * rand() * (1.0 / RAND_MAX);
}