Оператор цикла с предусловием (while) 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Оператор цикла с предусловием (while)



В языке С/С++ основной конструкцией, управляющей повторением, служит оператор цикла с предусловием while (пока). Он имеет следующий формат:

 

while (выражение)

оператор

(тело цикла);

 

Выражение обязательно должно быть заключено в круглые скобки. Оно понимается в широком смысле и в общем случае может быть как выражением логического типа (чаще всего), так и произвольным выражением (см. также оператор if). Тело цикла представляет простой или составной оператор.

Действие оператора while состоит в том, что тело цикла выполняется многократно до тех пор, пока значение выражения остаётся истинным (т. е. не равным нулю).

Выполнение оператора начинается с вычисления выражения в скобках. Если оно изначально ложно, то тело оператора вообще не выполняется и управление сразу передаётся на следующий за телом цикла оператор программы. Если выражение истинно, то выполняется тело цикла. После каждого очередного выполнения тела цикла значение выражения вычисляется заново. Процесс повторяется до тех пор, пока выражение не станет ложным (равным нулю). (Ясно, что в теле цикла должны быть предписания, влияющие на значения этого выражения).

Оператор цикла while может также завершиться "досрочно", при выполнении операторов break, goto, return, встретившихся в теле цикла.

 

Пример. Следующая программа вводит по запросу натуральное число, не превосходящее 65535 и формирует новое число, состоящее из тех же цифр, что и исходное, но взятых в обратном порядке.

 

//Перевернуть число

#include <conio.h>

#include<stdio.h>

#include <iostream.h>

int main()

{

unsigned int n;

long m=0;

cout << "Введи натуральное число <=65535 ";

cin >> n;

while (n)

{

m=m*10+n%10;

n=n/10;

}

cout<<"Перевернутое " << m;

getch();return 0;

 

}

 

 

Цикл while последовательно выделяет каждую цифру исходного числа, начиная с последней; для выделения цифр применяется операция вычисления остатка от деления целых чисел (n % 10). Формирование нового числа обеспечивает оператор

 

m = m * 10 + n % 10;

 

При делении целых чисел любая дробная часть отбрасывается, поэтому после оператора

 

n = n / 10;

 

исходное число уменьшается в 10 раз при каждом "обороте" цикла (отбрасывается последняя, уже использованная цифра), пока, наконец, не станет равным нулю. После этого цикл завершается и на экран монитора выдается значение переменной m, по внешнему виду перевернутое по отношению к исходному.

 

Рассмотрим еще один пример.

 

Приведенная ниже программа реализует алгоритм разложения произвольного натурального числа на простые множители. Для сокращения времени счета отдельно рассматривается случай множителя, равного двум (первый while-цикл), чтобы в последующем подвергать проверке только нечетные множители.

 

// Разложить число на простые множители

#include <conio.h>

#include<stdio.h>

#include <iostream.h>

int main()

{

int m;

cout<<"Введи натуральное число ";

cin>>m;

cout<<m<<"=1";

while (m%2==0)

{ cout<<"*2";

m=m/2;

}

 

int i=3;

while (i<=m)

{

if (m%i==0)

{

cout<<"*"<<i;

m=m/i;

}

Else

i=i+2;

}

getch(); return 0;

}

 

Протокол выполнения программы:

 

Введи натуральное число 5784

5784 = 1*2*2*2*3*241

 

 

Одна из самых неприятных особенностей операторов повторения – возможность образования бесконечного цикла ("зацикливания программы"). Чаще всего это происходит из-за отсутствия или недостижимости операторов модификации параметров цикла. Как следствие - условие завершения цикла навсегда остаётся неизменным.

Вот простой пример такой ошибки, приводящей к “зацикливанию”:

 

int k = 1; char ch = 02;

while (k <= 23);

{ cout << ch << endl;

k++;

}

 

Истинный виновник – точка с запятой (;) в заголовке цикла while. Формально телом цикла является пустой оператор, а составной оператор, заключённый в фигурные скобки, остаётся недоступным. Значение k не увеличивается и логическое условие k <= 23 всегда истинно.

 

Оператор цикла с постусловием (do_while).

 

Синтаксис оператора:

 

do

оператор

(тело цикла)

 

while (выражение);

 

 

Вначале выполняется тело цикла (а это простой или составной оператор), затем вычисляется выражение, трактуемое как логическое. Если значение выражения ложно (равно нулю), то оператор do_while завершается и управление передаётся оператору, следующему за while. Если же значение выражения истино (не равно нулю), то тело цикла выполняется снова, а затем снова вычисляется выражение.

Выполнение тела цикла оператора do_while повторяется до тех пор, пока выражение не станет ложным (равным нулю).

Оператор do-while может также завершиться "досрочно", при достижении одного из операторов break, goto или return в теле цикла.

 

Этот тип цикла целесообразно использовать, когда некоторое событие в теле цикла должно произойти раньше, чем осуществится проверка условия завершения цикла (например, ввод данных, запрос о целесообразности продолжения работы и др.).

 

Пример. Программа проверяет знание пользователем школьной таблицы умножения: случайным образом генерирует вопрос из таблицы умножения, проверяет правильность ответа, и даёт возможность пользователю продолжить тест или выйти из него.

 

#include <iostream.h>

#include <conio.h>

#include <stdlib.h>

#include <time.h>

using namespace std;

int main()

{

int a,b,x,p=0,n=0;

char ch;

srand((unsigned)time(NULL));

cout << "Если ты знаешь таблицу умножения, \n";

do

{

a=rand()%9+1; b=rand()%9+1;

cout << "ответь, чему равно " << a << '*' << b << '?';

cin >> x;

if (a*b == x)

{ cout << "ПРАВИЛЬНО"<<endl; p++;}

else

{ cout << "НЕВЕРНО, должно быть "<<a*b<<endl; n++; }

cout << "Нажми Enter для продолжения, иначе - любую клавишу ";

ch=getch();

}

while(ch == '\015');

 

cout << "правильных ответов - "<< p <<endl;

cout << "неправильных ответов - "<< n <<endl;

getch(); return 0;

}

 

Обратите внимание, что для продолжения тестирования следует каждый раз нажимать клавишу Enter. Её код в таблице ASCII – восьмеричное 015.

 

Эту разновидность оператора повторения следует применять с некоторой осторожностью.

 

Пусть требуется вычислить сумму N членов гармонического ряда (величина N заранее неизвестна и может быть результатом работы предыдущей части программы):

 

S = 1 +

 

Естественным представляется такое решение:

 

 

float Sum=0, N, i=1;

printf("Введи N ");

scanf("%f", &N);

while (i<=N)

{

Sum=Sum + 1/i;

i++;

}

printf("Сумма=%f\n", Sum);

 

 

Программист ради экономии одной переменной предпочтёт, быть может, суммирование N слагаемых гармонического ряда в обратном порядке (тогда N можно будет использовать как параметр цикла):

 

S = + + 1

 

 

float Sum=0, N;

printf("Введи N ");

scanf("%f", &N);

while (N)

{

Sum =Sum + 1/N;

N--;

}

printf("Сумма=%f\n", Sum);

 

 

В обоих случаях программа дает правильный результат, в том числе и при N=0.

 

Наконец, перепишем последний вариант суммирования членов ряда с помощью оператора цикла do-while. Убедитесь сами в том, что следующая программа имеет серьезный изъян: при N = 0 её поведение непредсказуемо.

 

 

float Sum=0, N;

printf("Введи N ");

scanf("%f", &N);

do

{

Sum = Sum + 1/N;

N--;

}

while( N);

printf("Сумма=%f\n", Sum);

 

 

Оператор цикла for

 

Оператора for является, пожалуй, самой популярной конструкцией для организации циклов. В общем случае его можно представить так:

 

for (выражение_1; выражение_2; выражение_3)

оператор(тело цикла);

 

Традиционно выражение_1 выполняет начальную инициализацию параметров цикла; выражение_2 трактуется всегда как логическое и служит для проверки условия завершения цикла; выражение_3 обеспечивает модификацию параметров цикла.

Перед вхождением в цикл вычисляется выражение_1 (один–единственный раз за время работы цикла). Затем оценивается значение выражения_2. Если оно истинно (не ноль), то выполняется оператор (тело цикла), после чего сразу вычисляется выражение_3 и снова оценивается выражение_2. Многократное повторение тела цикла происходит до тех пор, пока выражение_2 не станет ложным (равным нулю); на этом цикл завершается и управление передаётся оператору программы, следующему за оператором цикла.

 

Формально оператор for эквивалентен такой последовательности операторов:

 

выражение_1;

while (выражение_2)

{

Оператор(тело цикла);

выражение_3;

}

 

Циклически повторяемый участок, таким образом, включает не только оператор (простой или составной), заданный после заголовка for, но и выражение_3, которое вычисляется всякий раз перед проверкой выражения_2.

 

Как выражение_3, так и выражение_1 могут быть любой (в том числе и пустой) последовательностью простых операторов, разделяемых оператором "запятая". Любое из трёх или все три выражения в операторе for могут отсутствовать, однако, разделяющие их точки с запятыми (;) опускать нельзя.

Если опущено выражение_2, то считается, что оно постоянно истинно. Допускается даже такой вырожденный случай: for(;;); Он представляет собой бесконечный цикл, эквивалентный оператору while(1);.

 

Использовать ли while или for – во многом дело вкуса. В одних случаях более естественным выглядит цикл while, в других – цикл for. В следующем примере оператор for служит для организации и печати последовательных чисел Фибоначчи, не превосходящих заданного числа m. Напомним, что последовательность чисел Фибоначчи имеет вид:

 

1, 1, 2, 3, 5, 8, 13, 21, …

 

(Т. е. два первых числа равны единице, а каждое следующее равно сумме двух предыдущих).

 

// числа Фибоначчи

#include <stdio.h>

int main()

{

int m, k, j=1;

printf("введи m ");

scanf("%d",&m);

for (k=1; k<=m; k=k+j)

{

printf(" %d", k);

j=k-j;

}

}

 

Протокол выполнения программы:

введи m 40

1 1 2 3 5 8 13 21 34

 

 

Оператор k=1 в заголовке цикла for выполняется лишь однажды. Проверка k<=m происходит перед каждым (в том числе и перед первым) выполнением тела цикла. Тело цикла выполняется, если проверяемое условие истинно. Затем следует изменение параметра: k=k+j; этот оператор, кстати, формирует очередное число Фибоначчи, а оператор j=k-j хранит предыдущее.

 

Операторы break и continue

 

Оператор break может встречаться в теле цикла любого из операторов while, do-while или for и в операторе switch. При достижении этого оператора управление немедленно передаётся на оператор, следующий за оператором, в теле которого оператор break встретился.

 

Синтаксис: break;

 

Оператор continue может встретиться только в теле цикла одного из операторов while, do-while и for.

Синтаксис: continue;

 

Он передаёт управление на очередную итерацию в операторах цикла. Остающиеся в теле цикла операторы при этом игнорируются. В операторах while и do-while очередная итерация начинается с вычисления условного выражения. Для оператора for следующая итерация начинается с вычисления выражения_3 (см. выше), а затем происходит вычисление выражения_2 (условие продолжения цикла).

 

Следующий пример демонстрирует применение оператора break для немедленного прекращения работы оператора цикла while. Программа случайным образом генерирует целое число от 1 до 9 и предлагает пользо­ва­те­лю угадать его. Если пользователь не угадал, программа просит повторить попытку, а в случае удачи — прекращает работу и сообщает о количестве сделанных попыток.

#include <stdlib.h>

#include <iostream.h>

#include <conio.h>

#include <time.h>

using namespace std;

int main()

{

int x, answer, k = 0;

srand((unsigned)time(NULL)); //базовое число

x = rand()%9+1; //rand()=случайное число от 0 до RAND_MAX(32767)

// Вообще: если А, В -границы диапазона случайных чисел

// то x=A+(int)((B-A+1)*rand()/(RAND_MAX+1.0))

// в частности, x=1+(int)((9.0)*rand()/(RAND_MAX+1.0));

while (1)

{

cout<<"Угадай число от 1 до 9: ";

k++; cin>>answer;

if (x == answer)

{cout<<"Ты угадал за "<<k<<" попыток!"<<"\n";

break;}

Else

cout<<"Не угадал! Попробуй ещё раз!"<<"\n";

}

getch(); return 0;

}

 

В программе решающую роль играет пара srand() и rand(). Функция srand () выполняет инициализацию генератора случайных чисел. Функция rand () возвращает в качестве результата псевдослучайное целое число между 0 и RAND_MAX(32767).

 

Посмотрите, как выглядит оператор break в теле оператора цикла for. Рассмотрим такой пример.

 

Груз перевозят в трех- и пятитонных грузовиках. Какое минимальное количество машин понадобится для перевозки Т тонн груза?

Пусть x и y - искомое количество трех- и пятитонных грузовиков соответственно. Тогда, очевидно, задача сводится к решению уравнения с двумя неизвестными:

 

3x + 5y = T

Это так называемое диофантово уравнение. Известно, что оно всегда имеет решение при T ≥ 8. Будем решать его перебором всех возможных вариантов. Очевидно, что найденное решение оптимально, когда удается обойтись только пятитонными грузовиками. В общем же случае требуется минимизировать количество трехтонных. Отсюда следует, что начальное значение параметра цикла x должно равняться нулю, а вот предельное значение x не может превышать величину Т / 3.

Ниже приведен текст программы, которая реализует этот замысел. Обратите внимание, что как только будет обнаружена первая же пара x, y, удовлетворяющая уравнению 3x + 5y = T, как цикл немедленно завершается оператором break.

 

 

// Программа отлажена в Borland C++ Builder 6 (11.04.2008)

#include <stdio.h>

#include <conio.h>

#pragma argsused

#include "d:\\My_C_Builder_6\\rus.h"

int main()

{

int x,y,T; clrscr();

printf(rus("сколько груза перевозить(>=8т)?"));

scanf("%d",&T);

for(x=0; x<=T/3; x=x+1)

{

y=(T - 3*x)/5;

if(3*x + 5*y==T)

{

printf(rus("трехтонных: %d "),x);

printf(rus(" пятитонных: %d\n"),y);

break;

}

}

if(x > T/3)

printf(rus(" Нет решения "));

getch(); return 0;

}

 

 

Протокол работы программы:

 

сколько груза перевозить(>=8т)? 51

трехтонных: 2 пятитонных: 9

 

Продемонстрируем теперь возможности, которые предоставляет оператор "запятая " в заголовке оператора for.

Пусть требуется вычислить с заданной точностью = 10-7 приближенное значение числа p, используя сумму следующего бесконечного знакочередующегося ряда:

 

= 1 – + – … +(–1)n + …

Математическое обоснование этой формулы можно найти в соответствующих источниках (см. например, [ 17]) или в нашем пособии в разделе " Итерационные циклы".

При такой постановке задачи необходимое число слагаемых (а с ним и число оборотов цикла), необходимых для вычисления π с заданной точностью, неизвестно. По условию задачи считается, что нужное приближение получено, если вычислена сумма нескольких первых слагаемых, и очередное слагаемое оказалось по модулю меньше, чем заданная величина ε. Это слагаемое и все последующие слагаемые уже не надо учитывать.

Когда число повторений цикла заранее не известно и вычисления завершаются при достижении определенной точности вычислений, то традиционно используется оператор цикла while. Реализуем соответствующую программу.

 

#include <stdio.h>

#include <math.h>

#include <conio.h>

int main()

{

double s, eps=1.0E-7;

double t,z;

t=1; z=1; s=0;

while (fabs(t/z)>eps)

{

s=s + t/z; // новая частичная сумма ряда

t=-t; // перемена знака

z=z + 2; // новый знаменатель

}

printf("pi = %10.7lf (с точностью до %10.7lf)", s*4, eps);

getch();return 0;

}

 

Результат работы программы:

Pi = 3.1415925 (с точностью до 0.0000001)

 

При выполнении этой программы переменная z последовательно принимает значения 1, 3, 5,…, очередное слагаемое принимает значения 1, , а переменная S, в свою очередь, принимает значения 1, 1 1

Один из наиболее важных моментов при составлении циклических программ подобного рода – это присваивание переменным нужных начальных значений перед выполнением оператора цикла. Эти значения требуют тщательного подбора.

 

Но оператор for в С/С++ обладает той же мощностью, что и while (в отличие, скажем, от аналогичных операторов языка Паскаль). Перепишем предыдущую программу, используя оператор for. Перенесем, кроме того, в заголовок цикла for выражения, разделяемые знаком "запятая", для установки начальных значений используемых в цикле переменных (выражение_1) и для их модификации (выражение_3).

 

#include <stdio.h>

#include <math.h>

#include <conio.h>

int main()

{

 

for(double t=1, eps=1E-7, z=1, s=0; fabs(t/z)>eps; s+=t/z,t=-t,z+=2)

continue;

printf(" С точностью до %10.7lf pi = %10.7lf", eps, s*4);

getch();return 0;

}

Результат работы программы:

С точностью до 0.0000001 pi = 3.1415925

Считается, что мало не всю программу на языке С/С++ можно таким образом оформить в виде одного-единственного оператора цикла for. Но такой стиль программирования вряд ли стоит рекомендовать.

Снова обратимся к синтаксису оператора for. В скобках после ключевого слова for можно использовать любые выражения. Однако выражение_1 и выражение_3 не обязаны присваивать начальные значения и изменять параметры цикла по законам арифметических прогрессий, а выражение_2 не обязано быть проверкой условия. Но компилятор всегда интерпретирует выражение_2 как истину или ложь (ложь - двоичный ноль, истина - все остальное).

Пример. Снова составим программу, позволяющую угадать задуманное компьютером целое число от 1 до 9. На этот раз программа получилась более компактной благодаря замечательным свойствам оператора for и … менее понятной.

 

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <time.h>

#include "d:\\My_C_Builder_6\\rus.h"

 

int main()

{

int k= 0, x, answer = 0;

srand((unsigned)time(NULL));

clrscr(); //Очистка экрана

x =rand()%9+1;

for (printf(rus("Угадай число от 1 до 9\n")); answer!= x;

scanf("%d",&answer))

if (++k>1) printf(rus("Нет... А ну-ка ещё\n"));

printf(rus("Ты угадал за %d попыток\n"),k);

getch();return 0;

}

 

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

 

Преимущество оператора for - компактность и объединение в одной строке трех операторов управления циклом - особенно заметно проявляется во вложенных циклах. Так традиционно называют циклы, в теле которых, в свою очередь, тоже имеются циклы. При организации таких циклов следует помнить, что при каждом обороте внешнего цикла параметры каждого внутреннего (вложенного) цикла должны воспроизводиться (при этом часто внутренний цикл использует уже изменившиеся параметры внешнего цикла). Именно это обстоятельство обеспечивает оператору for некоторое преимущество перед другими формами оператора повторения.

 

Пример. Три натуральных числа a, b, c, удовлетворяющие равенству a2 + b2 = c2 , называются пифагоровыми числами. Составить программу, определяющую все пифагоровы числа при 1<= a<= M, 1<= b <= M, где M - произвольное натуральное число, M<=32767.

 

#include<stdio.h>

#include<conio.h>

#include<math.h>

int main()

{

double a,b,c; int M; clrscr();

printf ("Введи M "); scanf("%d",&M);

for(a=1; a <= M; a++)

for(b=a; b <= M; b++)

{

c=sqrt(a*a + b*b);

if(c==ceil(c))

printf("%6.0f^2 + %6.0f^2 = %6.0f^2\n",a, b, c);

}

getch();return 0;

}

 

Протокол выполнения программы:

 

Введи M 15

3^2 + 4^2 = 5^2

5^2 + 12^2 = 13^2

6^2 + 8^2 = 10^2

8^2 + 15^2 = 17^2

9^2 + 12^2 = 15^2

 

 

Следующая программа достаточно простая, хотя и содержит вложенные циклы. Она выводит на экран заполненный символом ª (код 06) равнобедренный треугольник, высота которого равна H. Во внешнем цикле устанавливается номер очередной строки вывода (параметр i). Затем в первом внутреннем (параметр k) в очередную строку выводится ровно 40 - i пробелов и вслед за этим второй внутренний цикл выводит в эту же строку 2*i – 1 символов ª, после чего курсор переводится на новую строку.

 

// Вершина треугольника расположена в точке

// экрана с координатами (40, 1)

# include <iostream.h>

# include <stdio.h>

# include <conio.h>

 

int main()

{

int H; int i,k;

char ch = 06; clrscr();

cout<<"Введи число строк ==> ";

cin>>H;

for(i=1; i<= H; i++)

{

for(k=40-i; k>0; k--)

cout << ' ';

for(k=1; k<=2*i-1; k++)

cout << ch;

cout<<"\n";

}

getch();return 0;

}

 

Протокол выполнения программы:

Введи число строк ==> 6

ª

ª ª ª

ª ª ª ª ª

ª ª ª ª ª ª ª

ª ª ª ª ª ª ª ª ª

ª ª ª ª ª ª ª ª ª ª ª

 



Поделиться:


Последнее изменение этой страницы: 2016-12-16; просмотров: 529; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.147.205.154 (0.231 с.)