Освой самостоятельно С++ за 21 день - Джесс Либерти 21 стр.


Анализ: Программа, представленная в листинге 7.15, позволяет найти значение любого члена ряда Фибоначчи. Использование рекурсии заменено циклом, организованным с помощью конструкции for. Кроме того, применение цикла уменьшает объем используемой памяти и время выполнения программы.

В строке 13 пользователю предлагается ввести порядковый номер искомого члена ряда Фибоначчи. Для нахождения этого значения используется функция fib(), в качестве параметра которой передается введенный порядковый номер. Если он меньше трех, функция возвращает значение 1. Для вычисления значений, порядковый номер которых превышает 2, используется приведенный ниже алгоритм.

1. Пpиcвaивaютcянaчaльныeзнaчeнияпepeмeнным:minusTwo=1, minus0ne=1, answer=2. Значение переменной, содержащей номер искомой позиции, уменьшается на 3, поскольку две первые позиции обрабатываются выше.

2. Для каждого значения n вычисляем значение очередного члена последовательности. Делается это следующим образом:

• переменной minusTwo присваивается значение переменной minusOne;

• переменной minusOne присваивается значение переменной answer;

• значения переменных minusOne и minusTwo суммируются и записываются в answer;

• значение n уменьшается на единицу.

3. Как только n достигнет нуля, возвращается значение переменной answer.

Следуя описанному алгоритму, можно воспроизвести на листе бумаги ход выполнения программы. Для нахождения, к примеру, пяти первых членов последовательности на первом шаге записываем

1, 1, 2,

Остается определить еще два члена ряда. Следующий член будет равен (2+1=3), а для вычисления искомого члена теперь нужно сложить значения только что полученного члена и предыдущего - числа 2 и 3, в результате чего получаем 5. В сущности, на каждом шаге мы смещаемся на один член вправо и уменьшаем количество искомых значений.

Особое внимание следует уделить выражению условия продолжения цикла for, записанному как n. Это одна из особенностей синтаксиса языка C++. По-другому это выражение можно представить в виде n'=0. Поскольку в C++ число 0 соответствует значению false, при достижении переменной n нуля условие продолжения цикла не будет выполняться. Исходя из сказанного, описание цикла может быть переписано в виде

for (n-=3; n!=0; n--)

Подобная запись значительно облегчит его восприятие. С другой стороны, первоначальный вариант программы иллюстрирует общепринятую для C++ форму записи условия, поэтому не стоит умышленно ее избегать.

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

Испытывая программу, не вводите слишком большие номера членов ряда Фибоначчи. Значения членов ряда возрастают довольно быстро и ввод большого порядкового номера может привести к переполнению регистра памяти.

Оператор switch

На занятии 4 вы познакомились с операторами if и if/else. Однако в некоторых ситуациях применение оператора if может привести к возникновению конструкций с большим числом вложений, значительно усложняющих как написание, так и восприятие программы. Для решения этой проблемы в языке C++ предусмотрен оператор switch. Основным его отличием от оператора if является то, что он позволяет проверять сразу несколько условий, в результате чего ветвление программы организуется более эффективно. Синтаксис оператора switch следующий:

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

{

case ПервоеЗначение: оператор;

break;

case ВтороеЗначение: оператор;

break;

....

case Значение_N: оператор:

break;

default: оператор;

}

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

Оператор switch

Синтаксис использования оператора switch следующий:

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

{

case ПервоеЗначение: оператор;

break;

case ВтороеЗначение: оператор;

break;

....

case Значение_N: оператор:

break;

default: оператор;

}

Оператор switch позволяет осуществлять ветвление программы по результатам выражения, возвращающего несколько возможных значений. Значение, возвращенное выражением, заданным в скобках оператора switch, сравнивается со значениями, указанными за операторами case, и в случае совпадения значений выполняется выражение в строке соответствующего оператора case. Будут выполняться все строки программы после выбранного оператора до тех пор, пока не закончится тело блока оператора switch, или не повстречается оператор break.

Если ни одно из значений операторов case не совпадет с возвращенным значением, то выполняются строки программы, стоящие после оператора default, в случае же отсутствия этого оператора в теле блока switch. управление будет передано следующей за этим блоком строке программы.

Пример 1:

switch (choice)

{

case 0:

cout << "Zero!" << endl;

break;

case 1:

cout << "One!" << endl;

break;

case 2:

cout << "Two!" << endl;

break;

default:

cout << "Default!" << endl;

break;

}

Пример 2:

switch (choice)

{

case 0:

case 1:

case 2:

cout << "Less than 3!" << endl;

break;

case 3:

cout << "Equals 3!" << endl;

break;

default:

cout << "Greater that 3!" << endl;

}

При отсутствии оператора break после оператора или выражения, следующего за case, будет выполняться выражение очередного блока case. В большинстве случаев такая ситуация возникает, когда оператор break пропущен по ошибке. Поэтому, если break опускается умышленно, рекомендуем вставлять в соответствующую строку комментарий. Пример использования оператора switch приведен в листинге 7.16.

Листинг 7.16. Использование оператора switch

1: //Листинг 7.16.

2: // Использование оператора switch

3:

4: #include <iostream.h>

5:

6: int main()

7: {

8: unsigned short int number;

9: cout << "Enter а number between 1 and 5: ";

10: cin >> number;

11: switch (number)

12: {

13: case 0: cout << "Too small, sorry!";

14: break;

15: case 5: cout << "Good job!\n"; // fall through

16: case 4: cout << "Nice Pick!\n"; // fall through

17: case 3: cout << "Excellent!\n"; // fall through

18: case 2: cout << "Masterful!\n"; // fall through

19: case 1: cout << "Incredible!\n";

20: break;

21: default: cout << "Too large!\n";

22: break;

23: }

24: cout << "\n\n";

25: return 0;

26: }

Результат:

Enter a number between 1 and 5: 3

Excellent!

Masterful!

Incredible!

Enter a number between 1 and 5: 8

Too large!

Анализ: Сначала программа предлагает ввести число. Затем введенное число обрабатывается оператором switch. Если вводится 0, то это соответствует значению оператора case из строки 13 и на экран выводится сообщение Too small, sorry!, после чего оператор break завершает выполнение конструкции switch. Если вводится число 5, управление передается в строку 15 и выводится соответствующее сообщение. Затем выполняется строка 16, в которой также выводится сообщение, и так до строки 20. В этой строке оператор break завершает выполнение блока с оператором switch.

Таким образом, при вводе чисел от 1 до 5 на экран будет выводиться ряд сообщений. Если же вводится число, превышающее 5, выполняется строка 21 с оператором default, в которой выводится сообщение Too large!.

Обработка комманд менб с помощью оператора switch

Вернемся к теме циклов с оператором for(;;). Такие конструкции называют бесконечными циклами, поскольку, если выполнение такого цикла не прервать оператором break, он будет работать бесконечно. Циклы подобного типа удобно использовать для обработки команд меню (листинг 7.17). Пользователь выбирает одну из предложенных команд, затем выполняется определенное действие и осуществляется возврат в меню. Так продолжается до тех пор, пока пользователь не выберет команду выхода.

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

Листинг 7.17. Пример бесконечного цикла

1: //Листинг 7.17.

2: //Обработка диалога с пользователем

3: //посредством бесконечного цикла

4: #include <lostream,h>

5:

6: // прототипы функций

7: int menu();

8: void DoTaskOne();

9: void DoTaskMany(int);

10:

11: int main()

12: {

13:

14: bool exit = false;

15: for (;;)

16: {

17: int choice = menu();

18: switch(choice)

19: {

20: case (1):

21: DoTaskOne();

22: break;

23: case (2):

24: DoTaskOne(2);

25: break;

26: case (3):

27: DoTaskOne(3);

28: break;

29: case (4):

30: continue;

31: break;

32: case (5):

33: exit=true;

34: break;

35: default :

36: cout << " Please select again!\n";

37: break;

38: } // конец блока switch

39:

40: if (exit)

41: break;

42: } // и так до бесконечности

43: return 0;

44: } // конец функции main()

45:

46: int menu()

47: {

48: int choice;

49:

50: cout << " **** Menu **** \n\n";

51: cout << "(1) Choice one\n";

52: cout << "(2) Choice two\n";

53: cout << "(3) Choice three\n";

54: cout << "(4) Redisplay menu.\n";

55: cout << "(5) Quit.\n\n";

56: cout << ": ";

57: cin >> choice;

58: return choice;

59: }

60:

61: void DoTaskOne()

62: {

63: cout << "Task One!\n";

64: }

65:

66: void DoTaskMany(int which)

67: {

68: if (which == 2)

69: cout << "Task Two!\n";

70: else

71: cout << "Task Three!\n";

72: }

Результат:

**** Menu ****

(1) Choice one.

(2) Choice two.

(3) Choice three.

(4) Redisplay menu.

(5) Quit.

: 1

Task One!

**** Menu ****

(1) Choice one.

(2) Choice two.

(3) Choice three.

(4) Redisplay menu.

(5) Quit.

: 3

Task Three!

**** Menu ****

(1) Choice one.

(2) Choice two.

(3) Choice three.

(4) Redisplay menu.

(5) Quit.

: 5

Анализ: В данной программе используются многие средства программирования, рассмотренные на этом и предыдущих занятиях. Тут же вы найдете пример использования конструкции switch.

Работа бесконечного цикла начинается в строке 15. Функция menu() обеспечивает вывод на экран команд меню и возвращает номер выбранной пользователем команды. Обработка введенного номера осуществляется в конструкции switch в строках 18-38.

При выборе первой команды управление передается следующему после строки case (1): оператору (строка 21). Далее, в строке 21, вызывается функция DoTaskOne(), которая выводит на экран сообщение о выборе пользователя. После завершения работы функции осуществляется возврат в точку вызова и выполняется оператор break (строка 22). Оператор break прерывает работу блока switch и управление передается в строку 39. Далее, в строке 40, проверяется значение переменной exit. Если оно истинно, бесконечный цикл прерывается оператором break в строке 41. В противном случае выполняется следующая итерация цикла (строка 15).

Особое внимание следует уделить оператору continue в строке 30. Внимательно проанализировав структуру программы, вы заметите, что этот оператор можно опустить, причем работа программы не изменится. Если бы строки с этим оператором не было, выполнялся бы оператор break, затем оператор if и, так как переменная exit содержала бы значение false, запускалась следующая итерация цикла. Использование оператора continue просто позволяет перейти на новую итерацию без проверки значения exit.

Резюме

В языке C++ существует множество способов организации циклических процессов. Оператор while проверяет условие и, если оно истинно, передает управление телу цикла. В конструкции do...while условие проверяется уже после выполнения тела цикла. Оператор for позволяет инициализировать переменные цикла, после чего проверяется выполнение условия. Если оно истинно, выполняется тело цикла, а затем операция, являющаяся третьей частью заголовка конструкции for. Перед началом каждой следующей итерации условие проверяется заново.

Оператора goto следует по возможности избегать, поскольку он позволяет осуществить переход в любую точку программы, что значительно усложняет ее восприятие и анализ. С помощью оператора continue можно осуществить переход на следующую итерацию цикла while, do...while или for, а break позволяет мгновенно завершить работу цикла.

Вопросы и ответы

Как определить, какой из операторов, if/else или switch, лучше использовать в конкретной ситуации?

Если приходится использовать более двух вложений операторов if, то лучше воспользоваться конструкцией с оператором switch.

Как выбрать между операторами while и do...while?

Если тело цикла должно выполняться хотя бы один раз, используйте цикл do...while. Во всех остальных случаях используйте оператор while.

Как выбрать между операторами while и for?

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

В каких случаях лучше использовать рекурсию, а в каких итерацию?

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

Какой из операторов, for(;;) или while(true) работает эффективнее?

Существенного различия между ними нет.

Коллоквиум

В этом разделе предлагаются вопросы для самоконтроля и укрепления полученных знаний и приводится несколько упражнений, которые помогут закрепить ваши практические навыки. Попытайтесь самостоятельно ответить на вопросы теста и выполнить задания, а потом сверьте полученные результаты с ответами в приложении Г. Не приступайте к изучению материала следующей главы, если для вас остались неясными хотя бы некоторые из предложенных ниже вопросов.

Контрольные вопросы

1. Можно ли в цикле for инициализировать сразу несколько переменных-счетчиков?

2. Почему следует избегать использование оператора goto?

3. Можно ли с помощью оператора for организовать цикл, тело которого не будет выполняться?

4. Можно ли организовать цикл while внутри цикла for?

5. Можно ли организовать цикл, который никогда не завершится? Приведите пример.

6. Что происходит при запуске бесконечного цикла?

Упражнения

1. Каким будет значение переменной x после завершения цикла for (int x = 0; x < 100; x++)?

2. Создайте вложенный цикл for, заполняющий нулями массив размером 10x10.

3. Организуйте цикл for, счетчик которого изменяется от 100 до 200 с шагом 2.

4. Организуйте цикл while, счетчик которого изменяется от 100 до 200 с шагом 2.

5. Организуйте цикл do...while, счетчик которого изменяется от 100 до 200 с шагом 2.

6. Жучки: найдите ошибку в приведенном фрагменте программы.

int counter = 0;

while (counter < 10)

{

cout << "counter: " << counter;

}

7. Жучки: найдите ошибку в приведенном фрагменте программы.

for(int counter = 0; counter < 10; counter++);

cout << counter << " ";

8. Жучки: найдите ошибку в приведенном фрагменте программы.

int counter = 100;

while (counter < 10)

{

cout << "counter: " << counter;

counter--;

}

9. Жучки: найдите ошибку в приведенном фрагменте программы.

cout << "Enter а number between 0 and 5: ";

cin >> theNumber;

switch (theNumber)

{

case 0:

doZero();

case 1: // идем дальше

case 2: // идем дальше

case 3: // идем дальше

case 4: // идем дальше

case 5:

doOneToFive();

break;

default:

doDefault();

break;

}

Назад Дальше