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; 
}
nkirov@cpp % c++ coins5.cpp
nkirov@cpp % ./a.out
Enter coin name: dime
Value = 0.01
nkirov@cpp % ./a.out
Enter coin name: asd
asd is not a valid coin name
Value = 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; }

nkirov@cpp % c++ richter.cpp
nkirov@cpp % ./a.out
Enter a magnitude on the Richter scale: 6.5
Many buildings considerably damaged, some collapse
nkirov@cpp % ./a.out
Enter a magnitude on the Richter scale: 2.5
Generally not felt by people
nkirov@cpp % ./a.out
Enter a magnitude on the Richter scale: 10
Most structures fall
nkirov@cpp % ./a.out
Enter a magnitude on the Richter scale: -2
Negative numbers are not valid

** Вложени условни оператори.

Пример: Задача за определяне на годишен данък в САЩ.

  За несемейни (single)
Ако облагаемата сума е над но не повече от данъкът е върху сумата над
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;
}
nkirov@cpp % c++ tax.cpp
nkirov@cpp % ./a.out
Please enter your income: 30000
Please enter s for single, m for married: m
The tax is $4500
nkirov@cpp % ./a.out
Please enter your income: 30000
Please enter s for single, m for married: s
The tax is $5611.5

** Булеви (логически) операции

Логически константи - възможни са само две стойности:
истина да 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.
A
B
A and B
not (A and B)
not A
not B
not A or not B
true
true
true
false
false
false
false
true
false
false
true
false true true
false
true
false
true
true false true
false
false
false true
true true true

Преобразуване:
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"
с приложение на отложени пресмятания.
Ако държавата не е USA, останалите условия нямат смисъл и не се проверяват. Ако държавата е USA, останалите условая имат смисъл и се проверяват последователно.
** Оператор за цикъл for

Най-често използваната форма на цикъл е:
i = start;
while (i <= end)
{ . . .
i++;
}
За тази форма има специален оператор за цикъл:
for (i = start; i <= end; i++)
Синтексис:
for (израз1; израз2; израз3) { тяло }
израз1 -> израз2(true) -> тяло -> израз3 -> израз2(true) -> тяло -> израз3 -> израз2(true) 
-> тяло -> .... -> израз3 -> израз2(false) -> край на цикъла
// 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;
}
nkirov@cpp % c++ forfac.cpp
nkirov@cpp % ./a.out
Please enter a number: 10
10! = 3628800


Цикъл с намаляване на променливата на цикъла.
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);
Пример: Древните гърци са използвали прост алгоритъм за пресмятане на корен квадратен (Babylonian method).

Редицата
  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; }
nkirov@cpp % c++ sqroot.cpp
nkirov@cpp % ./a.out
Please enter a number: 2
The square root is 1.41421
nkirov@cpp % ./a.out
Please enter a number: 121
The square root is 11


** Вложени цикли

Пример: Таблица от стойности с крайна сума за вложени на влог пари с дадена годишна лихва и брой години. 
Таблицата показва съдбата на 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;
}
nkirov@cpp % c++ table.cpp
nkirov@cpp % ./a.out
 Rate   5 years  10 years  15 years  20 years  25 years  30 years
 5.00  12762.82  16288.95  20789.28  26532.98  33863.55  43219.42
 5.50  13069.60  17081.44  22324.76  29177.57  38133.92  49839.51
 6.00  13382.26  17908.48  23965.58  32071.35  42918.71  57434.91
 6.50  13700.87  18771.37  25718.41  35236.45  48276.99  66143.66
 7.00  14025.52  19671.51  27590.32  38696.84  54274.33  76122.55
 7.50  14356.29  20610.32  29588.77  42478.51  60983.40  87549.55
 8.00  14693.28  21589.25  31721.69  46609.57  68484.75 100626.57
 8.50  15036.57  22609.83  33997.43  51120.46  76867.62 115582.52
 9.00  15386.24  23673.64  36424.82  56044.11  86230.81 132676.78
 9.50  15742.39  24782.28  39013.22  61416.12  96683.64 152203.13
10.00  16105.10  25937.42  41772.48  67275.00 108347.06 174494.02


В някои случаи броят на итерациите (изпълнения на тялото на цикъла) на вътрешния цикъл зависи от броячът на итерациите на външния цикъл.

Пример:  Какво ще отпечати следващата програма?

// 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;
}

nkirov@cpp % c++ triangle.cpp
nkirov@cpp % ./a.out
Enter number of rows: 6
[]
[][]
[][][]
[][][][]
[][][][][]
[][][][][][]