Слово else относится к ближайшему сверху слову if, находящемуся в том же блоке инструкций, но еще не связанному ни с каким другим словом else.
Между словами ifи else должна находиться хотя бы одна инструкция. Поэтому в первой реализации последнего примера мы вынуждены были использовать так называемую “пустую инструкцию”, которая не имеет никакого изображения и располагается между записью выражения (p) и разделителем;. Вторая реализация этой схемы алгоритмы, основанная на инвертировании выражения p, является более корректной и эффективной.
В первой реализации последнего примера мы также использовали “пустую инструкцию”, так как после слова else(как и после слова if)также должна находиться хотя бы одна инструкция или блок инструкций. Если в первой реализации не записать слово else и пустую инструкцию вложенной инструкции if, а во второй реализации не оформить эту вложенную инструкцию if в виде блока, то будет реализована схема совершенно другого алгоритма:
В программах очень часто используется многоуровневое вложение if–инструкции так называемой “лесенкой”, схема алгоритма которой выглядит так:
Подобные схемы можно использовать для множественного выбора, однако для реализации такой схемы более подходит инструкция, рассмотренная в следующем параграфе.
40. Инструкция множественного выбора (switch)
Эта инструкция служит для ветвления программы во многих направлениях.
Ее синтаксис:
switch (<Выражение>)
{
case<Константа 1>:
<Последовательность инструкций 1>
break;
case<Константа 2>:
<Последовательность инструкций 2>
break;
……….
case<Константа N>:
<Последовательность инструкций N>
break;
default:
<Последовательность инструкций>
}
При совпадении значения выражения со значением одной из констант 1 – Nбудет выполнена соответствующая этой ветви последовательность инструкций. Инструкция break осуществляет прерывание выполнения инструкции switchи управление передается следующему за switch-инструкцией оператору. Если значение выражения не совпадет ни с одной из констант, то будут выполнены инструкции ветви default.
Ветвь defaultне обязательна. В случае отсутствия ветви defaultпри несовпадении значения выражения ни с одной из констант не будет выполнена ни одна из инструкций оператора switch.
Значение выражения в инструкции switchобязательно должно быть либо целого, либо символьного типа (в принципе тип выражения может быть и логическим, но в этом случае выгоднее пользоваться if-инструкцией)–вещественные значения не допускаются.
Пример записи инструкции:
unsignedi;
cin>>i;
Switch ( i )
{
case 0:
cout<< "ноль\n";
break;
case 1:
cout<< "один\n ";
break;
case 2:
cout<< "два\n ";
break;
default:
cout<< "много\n ";
}
Если в выбранной ветви будет отсутствовать инструкция break, то после выполнения инструкций этой ветви начнут выполняться инструкции следующей ветви до тех пор, пока не встретится инструкция breakили не будет достигнут конец оператораswitch. Например:
unsignedi;
cin>>i;
Switch ( i )
{
case 0: cout<< 0;
case 1: cout<< 1;
case 2: cout<< 2;
case 3: cout<< 3;
case 4: cout<< 4;
case 5: cout<< 5;
}
В этом примере на экран будет выведена последовательность цифр, начинающаяся с цифры, введенной с клавиатуры.
Инструкция switch более эффективна, чем структура “лесенка”, реализованная с помощью вложенных инструкций if.
41. Цикл с предусловием (while).
Формат записи этой инструкции:
Или, если тело цикла представляет собой одиночную инструкцию:
И тому и другому варианту соответствует следующая схема алгоритма:
Выражение в этой инструкции может быть любого типа, значения которого можно трактовать как значения логического типа данных (bool). Это выражение определяет условие продолжения выполнения тела цикла, то есть, если значение этого выражения истинно (trueили не равно 0), то тело цикла выполняется вновь, если же ложно (falseили 0) , то цикл заканчивается и управление передается следующей за циклом инструкции.
Очевидно, что тело цикла в этой инструкции может не выполниться ни разу, если при входе в цикл значение выражения будет соответствовать значению falseили 0.
Для того чтобы цикл начал выполняться, необходимо перед началом цикла выполнить инициализацию его параметров так, чтобы значение выражения соответствовало значению trueили было не равно 0.
Неправильное использование этой инструкции может привести к образованию бесконечного цикла (к зацикливанию программы). Такая ситуация возникает в том случае, когда значение выражения не меняется в процессе выполнения цикла. Для того чтобы избежать подобной ситуации, необходимо в теле цикла предусмотреть такие изменения параметров цикла, при которых, в конце концов, условие продолжения цикла перестанет выполняться, либо использовать принудительное завершение цикла с помощью инструкции break.
Рассмотрим некоторые примеры.
Пример 1. Необходимо в виде строки вывести на экран цифры от 0 до 9.
intk = 0; //На экран выведено k цифр
while (k<= 9) //Здесь используется логическое выражение
{
cout<<k;
++k;
}
//На экран выведено k = 10 цифр: 0123456789
Формулировка условия продолжения цикла в этом примере может быть и другой:k< 10или k != 9
Поскольку на каждом шаге цикла параметр цикла k увеличивает свое значение на 1 (начиная с 0), то после выполнения 10 шагов условие выполнения цикла (при любой формулировке из перечисленных) обязательно перестанет выполняться и цикл закончится.
Но вот, если в теле цикла не предусмотреть наращивание параметра k, то получим бесконечный цикл, в котором на экран будут выводиться одни нули:
int k = 0;
while (k <= 9)
{
cout<< k;
}
Для остановки его нам придется принудительно прервать выполнение программы.
Причиной зацикливания может быть и неправильная формулировка условия продолжения цикла.
Пример 2. Необходимо в виде строки вывести на экран только нечетные числа из первого десятка.
int k = 1;
while (k != 10)
{
cout<< k << “\t”;
k += 2;
}
В этом примере выражение k != 10никогда не станет ложным, так как параметр цикла kпри его увеличении на каждом шаге цикла на 2 будет иметь только нечетные значения. Правильной формулировкой условия является, например, такая: k< 10.
Пример 3. Для принудительного (досрочного) прекращения цикла можно использовать инструкцию break. Например:
while (<Выражение>)
{
<Инструкция 1>;
if (<Ошибка>)
break;
<Инструкция 2>;
}
<Инструкция 3>;
Если при выполнении <Инструкции1>возникает ошибка (о чем свидетельствует значение trueвыражения <Ошибка>), после которой выполнение цикла должно быть прекращено, выполняется инструкция break. При выполнении инструкции break цикл прекращается (<Инструкция 2>выполнена не будет), и управление передается <Инструкции 3>, следующей за оператором цикла.
Пример 4. Если в предыдущем примере при возникновении ошибки требуется только пропустить выполнение <Инструкции 2), а затем продолжить выполнение цикла, следует использовать инструкцию continue:
while (<Выражение>)
{
<Инструкция 1>;
if (<Ошибка>)
continue;
<Инструкция 2>;
}
При выполнении инструкции continue<Инструкция 2>выполнена не будет, но цикл перейдет к выполнению следующей итерации (шага).
Инструкция continue на практике используется достаточно редко, так как обойтись без нее очень просто:
while (<Выражение>)
{
<Инструкция 1>;
if (!<Ошибка>)
{
<Инструкция 2>;
}
}
Надо только не забыть инвертировать выражение <Ошибка>.
42. Цикл с постусловием (dowhile)
Формат записи этой инструкции:
Или, если тело цикла представляет собой одиночную инструкцию:
И тому и другому варианту соответствует следующая схема алгоритма:
Так же, как и в предыдущем цикле, выражение в этой инструкции может быть любого типа, значения которого можно трактовать как значения логического типа данных (bool). Это выражение определяет условие продолжения выполнения тела цикла, то есть, если значение этого выражения истинно (trueили не равно 0), то тело цикла выполняется вновь, если же ложно (falseили 0), то цикл заканчивается и управление передается следующей за циклом инструкции.
Принципиальным отличием этого цикла от предыдущего состоит в том, что тело цикла в этой инструкции обязательно будет выполнено хотя бы один раз.
Использование этого цикла проиллюстрировано следующим примером:
Пример 1. Необходимо в виде строки вывести на экран цифры от 0 до 9.
intk = 0; //На экран выведено k цифр
do
{
cout<<k;
++k;
}
while (k<= 9); //Здесь используется логическое выражение
//На экран выведено k = 10 цифр: 0123456789
Все остальное сказанное о предыдущем цикле, можно практически однозначно применить и к циклу с постусловием.
43. Итерационный цикл (for)
Формат записи этой инструкции:
Или, если тело цикла представляет собой одиночную инструкцию:
И тому и другому варианту соответствует следующая схема алгоритма:
При запуске цикла однократно выполняется Инициализация параметра (параметров) цикла, после чего осуществляется проверка Условия, определяющего необходимость выполнения тела цикла. После окончания выполнения инструкций тела цикла, на каждой итерации выполняется Модификацияпараметра (параметров) цикла и снова проверяется Условие. Так продолжается до тех пор, пока Условие не станет ложным (false).
Разделы Инициализации, Условия и Модификации в заголовке цикла разделяются символом ‘;’.
Пример записи (пример из предыдущего параграфа):
int k;
for (k = 0; k <= 9; ++k)
cout<< k;
Если параметр k цикла используется только внутри цикла (после выхода из цикла переменная kбольше не нужна), эту переменную можно (и лучше) определить непосредственно в разделе Инициализации цикла:
for (int k = 0; k <= 9; ++k)
cout<< k;
В разделах Инициализации и Модификации можно управлять сразу несколькими параметрами цикла:
for (int k = 1, n = 10; k <= 10; ++k, --n)
cout<< k << “ * ” << n << “ = ” << k * n <<endl;
На экран будет выведено:
1 * 10 = 10
2 * 9 = 18
3 * 8 = 24
4 * 7 = 28
5 * 6 = 30
6 * 5 = 30
7 * 4 = 28
8 * 3 = 24
9 * 2 = 18
10 * 1 = 10
Отдельные элементы разделов Инициализации и Модификации отделяются друг от друга символом ‘,’.
Любой раздел заголовка цикла может отсутствовать. Раздел Инициализации, например, может отсутствовать, когда начальные значения параметров цикла устанавливаются вне цикла, перед его началом. Модификация значений параметров цикла может осуществляться внутри тела цикла, а не в его заголовке. При отсутствии Условия продолжения выполнения цикла, цикл становится бесконечным и для выхода из него придется использовать инструкцию break. Однако, какой бы из разделов ни отсутствовал, соответствующие разделительные символы ‘;’в заголовке цикла должны обязательно присутствовать:
#include<conio.h>
……..
cout<< “Для продолжения работы нажмите любую клавишу…” <<endl;
for ( ; ! kbhit(); );
……..
В этом примере цикл, в заголовке которого отсутствуют разделы Инициализации и Модификации, используется для приостановки выполнения программы до нажатия на клавиатуре любой клавиши (функция kbhit() возвращает значение false, если на клавиатуре не нажата никакая клавиша, и значение true, если клавиша была нажата – для использования этой функции необходимо включить заголовочный файл conio.h).
Замечание. Приостановку работы программы значительно проще (без использования циклов) можно выполнить с помощью функции getch(), которая ожидает нажатия клавиши на клавиатуре и возвращает символ этой клавиши без отображения этого символа на экране (необходим заголовочный файл conio.h):
#include<conio.h>
……..
cout<< “Для продолжения работы нажмите любую клавишу…” <<endl;
getch();
……..
Принудительный выход из цикла forосуществляется с помощью инструкции break, а принудительный переход к следующей итерации (шагу цикла) – с помощью инструкции continue.
Тела циклов могут содержать любые инструкции языка C++, в том числе и другие циклы. Подобные конструкции называются вложенными циклами. Использование вложенных циклов является весьма распространенным приемом программирования при решении очень многих задач.
44. Инструкция перехода break/
45. Инструкция перехода continue/
46. Инструкция перехода goto/
Инструкция break осуществляет прерывание выполнения инструкциии управление передается следующему за инструкцией оператору. По поводу инструкции break следует напомнить, что при вложенных циклах она обеспечивает прекращение того цикла, в теле которого она непосредственно расположена.
Если при возникновении ошибки требуется только пропустить выполнение <Инструкции 2>, а затем продолжить выполнение цикла, следует использовать инструкцию continue:
while (<Выражение>)
{
<Инструкция 1>;
if (<Ошибка>)
continue;
<Инструкция 2>;
}
При выполнении инструкции continue<Инструкция 2>выполнена не будет, но цикл перейдет к выполнению следующей итерации (шага).
Инструкция continue на практике используется достаточно редко, так как обойтись без нее очень просто:
while (<Выражение>)
{
<Инструкция 1>;
if (!<Ошибка>)
{
<Инструкция 2>;
}
}
Характеризуя инструкции break(ее использование в циклах), continue и goto в целом, следует сказать, что их применение противоречит принципам структурного программирования и приводит к затруднению понимания алгоритмов программ, их отладки и дальнейшей модификации. Однако, несмотря на это, их использование в ряде случаев бывает оправдано. В принципе, как бы ни был сложен алгоритм программы, его всегда можно реализовать без использования этих инструкций. В основном это достигается за счет введения дополнительных логических переменных (флажков) и некоторого усложнения условий продолжения циклов. Однако в некоторых случаях эти “накладные расходы” оказываются чрезмерными и тогда выгоднее все-таки использовать эти инструкции перехода. Как поступать в тех или иных ситуациях во многом зависит от конкретного алгоритма и от внутренних предпочтений программиста. Но, все же, злоупотреблять использованием этих инструкций не следует.
Инструкция goto обеспечивает переход на выполнение инструкции отмеченной с помощью метки.
Формат записи: goto<Метка>;
Метка представляет собой некоторый идентификатор, за которымследует символ’:’. Меткой может быть помечена любая инструкция, находящаяся в той же функции, в которой находится оператор goto.
Пример использования:
…….
M1: <Инструкция>;
…….
if (<Условие>)
gotoM1;
…….
Наиболее часто обоснованное использование инструкции goto связано с выходом из глубоко вложенных циклов:
Использование в этом случае инструкции break вместо оператора gotoпривело бы к прерыванию только внутреннего цикла. Для прерывания выполнения всех циклов с помощью инструкции break потребовались бы существенные усилия.
47. Понятие рекуррентных вычислений, примеры.
Рекуррентные схемы используются при вычислении значений некоторой последовательности, в которой значение каждого очередного элемента определяется на основе значений одного или нескольких предыдущих элементов.
В общем случае схему рекуррентных вычислений можно представить следующим образом.
Пусть в некоторой последовательности известны первые nзначений:
Тогдаэлемент при определяется так:
Или, иными словами:
Простейшим примером рекуррентного соотношения является вычисление факториала числа:
Программная реализация вычисления факториала: