4.5.3. Оператор do/while

Използва се за реализиране на произволни циклични процеси. Ще го илюстрираме чрез пример, след което ще опишем неговите синтаксис и семантика. За целта ще използваме задача 37.

Програма Zad37_1.cpp реализира тази задача, като използва оператора do/while.
Program Zad37_1.cpp
#include <iostream.h>
#include <iomanip.h>
#include <math.h>
int main()
{cout << "x= ";
double x;
cin >> x;
if (!cin)
{cout << "Error, Bad input! \n";
return 1;
}
if (x < -1 || x > 1)
{cout << "Incorrect Input! \n";
return 1;
}
cout << "eps= ";
double eps;
cin >> eps;
if (!cin)
{cout << "Error, Bad input! \n";
return 1;
}
if (eps <= 0)
{cout << "Incorrect input! \n";
return 1;
}
double x1;
double x2 = x;
double s = x;
int i = 2;
do
{x1 = x2;
x2 = -x1 * x * x / (i*(i+1));
s = s + x2;
i = i + 2;
} while (fabs(x1-x2) >= eps);
cout << setprecision(5) << setiosflags(ios :: fixed);
cout << "s=" << setw(10) << s << "\n";
return 0;
}
Операторът do/while в нея е в одебелен шрифт. Започва със запазената дума do (прави, повтаряй следното), следва оператор (в случая блок), който определя действията, които се повтарят и затова се нарича тяло на цикъла. Запазената дума while (докато) отделя тялото на оператора от булевия израз fabs(x1-x2) >= eps. Последният е ограден в кръгли скобки и определя условието за завършване изпълнението на цикъла.
Ще проследим изпълнението на програмата за x = 1 и eps = 0.5.
След изпълнението на операторите за въвеждане и дефинициите на s, x1, x2 и i, състоянието на паметта е следното:
x eps x1 x2 s i
1.0 0.5 - 1.0 1.0 2
Изпълнението на оператора за цикъл започва с изпълнение на тялото на цикъла – блока
{x1 = x2;
x2 = -x1 * x * x / (i*(i+1));
s = s + x2;
i = i + 2;
}
след което се получава:
x eps x1 x2 s i
1.0 0.5 1.0 -0.16667 0.83333 4
Пресмята се стойността на булевия израз fabs(x1-x2) >= eps и тъй като тя е true, повторно се изпълняват операторите от блока, съставящ тялото. В резултат имаме:
x eps x1 x2 s i
1.0 0.5 -0.16667 0.00833 0.84167 6
Сега вече стойността на булевия израз, определящ условието за завършване, има стойност false. Изпълнението на оператора за цикъл завършва. С извеждането на стойността на сумата s завършва и изпълнението на програмата.
Забелязваме, че в тази програма, настройката на променливите x1 и x2 става в тялото на цикъла do/while, а не извън него. Това се обуславя от факта, че тялото на този вид цикъл поне веднъж ще се изпълни.
Описанието на синтаксиса и семантиката на оператора do/while е илюстрирано на Фиг. 11.


Синтаксис
do
<оператор>
while (<условие>);
където
- do (прави, повтаряй докато …) и while (докато) са запазени думи на езика.
- <оператор> е точно един оператор. Той описва действията, които се повтарят и се нарича тяло на цикъла.
- <условие> е булев израз. Нарича се условие за завършване на цикъла. Огражда се в кръгли скобки.
Семантика
Изпълнява се тялото на цикъла, след което се пресмята стойността на <условие>. Ако то е false, изпълнението на оператора do/while завършва. В противен случай се повтарят действията: изпълнение на тялото и пресмятане стойността на <условие>, докато стойността на <условие> е true. Веднага, след като стойността му стане false, изпълнението на оператора завършва.


Фиг. 11.
Забележки:
1. Между запазените думи do и while стои точно един оператор. Ако няколко действия трябва да се извършат, оформя се блок.
2. Дефинициите в тялото, не са видими в <условие>. Например, не е допустим фрагментът:
double x2 = x;
double s = x;
int i = 2;
do
{double x1 = x2;
x2 = -x1 * x * x / (i*(i+1));
s = s + x2;
i = i + 2;
} while (fabs(x1-x2) >= eps);
Компилаторът ще съобщи, че x1 не е дефиниран на линия:
} while (fabs(x1-x2) >= eps);
Следователно, всички променливи в <условие> трябва да са дефинирани извън оператора do/while.
3. Следствие разширената интерпретация на true и false, частта <условие> може да е аритметичен израз. Това е лош стил за програмиране и ние не го препоръчваме.
4. Операторът do/while завършва с ;.

Задачи върху оператора do/while

Задача 38. Да се напише програма, която намира произведението на целите числа от m до n, където m и n са дадени естествени числа и m ≤ n. За целта да се използва операторът do/while.

Програма Zad38.cpp решава задачата.
Program Zad38.cpp
#include <iostream.h>
int main()
{cout << "m= ";
int m;
cin >> m;
if (!cin)
{cout << "Error, Bad Input! \n";
return 1;
}
if (m <= 0)
{cout << "Incorrect Input! \n";
return 1;
}
cout << "n= ";
int n;
cin >> n;
if (!cin)
{cout << "Error, Bad Input! \n";
return 1;
}
if (n <= 0)
{cout << "Incorrect Input! \n";
return 1;
}
if (m > n)
{cout << "Incorrect Input! \n";
return 1;
}
int prod = 1;
int i = m;
do
{prod = prod * i;
i++;
} while (i <= n);
cout << prod << "\n";
return 0;
}
Тъй като е в сила релацията m ≤ n, произведението ще съдържа поне един елемент от редицата от цели числа {m, m+1, m+2, …, n}. Това прави възможно използването на оператора do/while.

Задача 39. Да се напише програма, в резултат от изпълнението на която се изяснява, има ли сред числата от серията: i3 – 3.i + n3, i = 1, 2, …, n, число, кратно на 5. Ако има, да се изведе true, иначе – false.

Решението на тази задача изисква последователно да се конструират елементите от серията числа. Това продължава до намиране на първото число, кратно на 5, или до изчерпване на редицата без число с това свойство да е намерено.

Програма Zad39_1.cpp е едно решение на задачата.
Program Zad39_1.cpp
#include <iostream.h>
int main()
{cout << "n= ";
int n;
cin >> n;
if (!cin)
{cout << "Error, Bad Input!!! \n";
return 1;
}
if (n <= 0 )
{cout << "Incorrect Input! \n";
return 1;
}
int i = 0;
int a;
do
{i++;
a = i*i*i - 3*i + n*n*n;
} while (a%5 != 0 && i < n);
if (a%5 == 0) cout << "true\n";
else cout << "false\n";
return 0;
}

Възникват два въпроса:
1) Ако условието условие (a%5 != 0 && i < n) стане лъжа, тъй като се излиза от цикъла, правилно ли следващият оператор определя резултата?
От законите на де Морган следва, че е истина a%5 == 0 || i = n. Ако a%5 == 0, тъй като a е i-тият елемент на серията и i ≤ n, наистина в серията съществува елемент с исканото свойство. Ако a%5 не е 0, следва че i = n ще е в сила, т.е. a е n – тият елемент и за него свойството не е в сила. Но тъй като са сканирани всички елементи от серията, наистина в нея не съществува елемент с търсеното свойство.
2) Ще се стигне ли до състояние, при което горното условие наистина ще е лъжа?
Условието (a%5 != 0 && i < n) ще е лъжа, ако a%5 == 0 || i = n е в сила и се достига или когато в серията има елемент с търсеното свойство, или е сканирана цялата серия и i указва последния й елемент. Ако в серията няма елемент с исканото свойство, тъй като i е инициализирано с 0 и се увеличава с 1 на всяка стъпка от изпълнението на програмата, в един момент ще стане вярно условието i = n, т.е. цикълът ще завърши изпълнението си.

Друго решение дава програмата Zad39_2.cpp. То не съдържа фрагментът, въвеждащ стойност на променливата n.

Program Zad39_2.cpp
#include <iostream.h>
int main()
{ …
int i = 1;
int a;
do
{a = i*i*i - 3*i + n*n*n;
i++;
} while (a%5 != 0 && i <= n);
if (a%5 == 0) cout << "true\n";
else cout << "false\n";
return 0;
}
Лошото при това решение, че в тялото на цикъла има разминаване на елемента от серията, запомнен в a, и поредния му номер.

Задача 40. Да се напише програма, която въвежда естествено число и установява, дали цифрата 5 участва в записа на числото.

Програма Zad40.cpp решава задачата.
Program Zad40.cpp
#include <iostream.h>
int main()
{cout << "n= ";
int n;
cin >> n;
if (!cin)
{cout << "Error, Bad Input!!! \n";
return 1;
}
if (n <= 0 )
{cout << "Incorrect Input! \n";
return 1;
}
int d;
do
{d = n % 10;
n = n / 10;
} while (d != 5n && n != 0);
if (d == 5) cout << "true\n";
else cout << "false\n";
return 0;
}
В тялото на цикъла последователно се намират цифрата на единиците на числото n и числото без цифрата на единиците си. Това продължава докато поредната цифра е различна от 5 и останалото число е различно от 0.

Задача 41. Нека a е неотрицателно реално число. Да се напише програма, която приближено пресмята квадратен корен от a по метода на Нютон.
Упътване: (метод на Нютон) Дефинира се редица от реални числа x0, x1, x2, x3, … по следния начин:


Сумирането да продължи докато абсолютната стойност на разликата на последните два констуирани елемента на редицата е по-малка от e, e>0 е дадено достатъчно малко реално число.

Програма Zad41.cpp решава задачата.
Program Zad41.cpp
#include <iostream.h>
#include <iomanip.h>
#include <math.h>
int main()
{cout << "a= ";
double a;
cin >> a;
if (!cin)
{cout << "Bad Input! \n";
return 1;
}
if (a < 0)
{cout << "Incorrect Input! \n";
return 1;
}
cout << "eps= ";
double eps;
cin >> eps;
if (!cin)
{cout << "Bad Input! \n";
return 1;
}
if (eps <= 0 || eps > 0.5)
{cout << "Incorrect Input! \n";
return 1;
}
double x0;
double x1 = 1;
do
{x0 = x1;
x1 = 0.5*(x0 + a / x0);
} while (fabs (x1-x0) >= eps);
cout << setprecision(6) << setiosflags(ios :: fixed);
cout << "sqrt(" << a << ")= " << setw(10) << x1 << "\n";
return 0;
}