Меньше или равно
Скачать 356 Kb.
|
7. Реализация разветвленных алгоритмов Основой разветвленных алгоритмов являются условия, в зависимости от истинности которых выполнение может идти различными способами (ветвями алгоритма). Условия записываются в виде логических операций и выражений. 7.1. Логические операции и выражения Логическая операция, как и любая другая, состоит из операндов, разделенных знаком операции. Для записи логических операций применяются следующие знаки: == - равно; != - не равно; < - меньше; > - больше; <= - меньше или равно; >= - больше или равно. Результатом таких операций является истина или ложь. Результат логической опреации может быть присвоен (операция =) переменной целого типа; в этом случае аналогами логических понятий будут служить следующие значения: истину обозначает любое ненулевое целое число, а ложь - ноль. Логические операции могут входить в состав логических выражений. которые представляют собой сложную операцию: в качестве операндов выступают "обычные" логические операции; знаками операции являются следующие дополнительные знаки: && - "логическое и": имеет два операнда (логических операции); результатом является истина, только если оба операнда истинны; || - "логическое или": имеет также два операнда; результатом является истина, если хотя бы один операнд истинен; ! - не (операция отрицания): имеет один операнд; результатом является истина, если операнд ложен, результатом является ложь, если операнд истинен. Логические операции также имеют порядок предшествования, который покажем по убыванию приоритета операций (сверху вниз:
Приведем примеры записи условий в виде логических операций и выражений: L >= Lmin ( L больше или равно Lmin) dx Если возникают трудности в понимании сложных выражений, полезно пользоваться круглыми скобками, например: (dx Рассмотрим также отличие операции D==0. от операции D=0. Логическая операция D==0. представляет собой проверку условия равенства переменной D нулю. Эту операцию можно прочитать так: равно ли D нулю? Операция же D=0. - типичная операция присваивания, при которой переменной D присваивается нулевое значение. 7.2. Конструкции if-else Самый простой вариант разветвленного алгоритма реализуется с помощью ключевого слова if (если): if(условие) оператор; Е сли условие выполняется, то управление передается оператору (см. фрагмент блок-схемы). В противном случае осуществляется переход к следующей части программы в обход данного оператора. Есть возможность выполнять не один, а группу операторов, тогда их необходимо заключить в фигурные скобки (выделить как блок): if(условие) { оператор1; оператор2; } Обратите внимание на запись текста с отступом, что позволяет более наглядно отображать структуру алгоритма, хотя формально можно было бы и записать: if(условие) {оператор1;оператор2;} Следует хорошо понимать, в каких местах указывается знак "точка с запятой". Он ставится после каждого оператора. Почему же он не ставился после записи "if(условие)"? Дело в том, что эта запись не является отдельным оператором. Оператором является вся конструкция "if" с указанием всех операторов, которые выполняются при истинности условия. Если бы, например, было ошибочно записано: if(условие); оператор; то оператор выполнялся бы в любом случае, и запись условия теряла бы смысл. Более сложный вариант разветвленного алгоритма реализуется с помощью конструкции if-else (если-иначе). При этом дополнительно указывается, какие операторы надо выполнить в случае ложности условия: if(условие) оператор1; else оператор2; Можно в каждой ветви задавать не один, а группу операторов, например: i f(условие) { оператор1; оператор2; } else { оператор3; оператор4; } Наконец, существует более сложный вариант конструкции if-else, позволяющий предусмотреть проверку дополнительных условий. Приведем эту конструкцию в общем виде с проверкой дополнительного условия и выполнением группы операторов в каждой ветви. i f(условие1) { оператор1; оператор2; } else if(условие2) { оператор3; оператор4; } else { оператор5; оператор6; } Таких дополнительных проверок условия может быть несколько; кроме того, в каждой ветви может быть любое количество операторов. Если ветвь включает только один оператор, его можно не заключать в фигурные скобки. Наконец, последняя ветвь (else) может отсутствовать. Рекомендуем читателю самостоятельно составить несколько вариантов разветвленных блок-схем и соответствующих операторов. Приведем также несколько примеров. Пример 7.1. Программа вычисления модуля целого числа. Математическая формулировка. b=|a| означает: b= Блок-схема алгоритма: И сходный текст программы: /* Определение модуля целого числа */ #include void main() { int a=-5, b; /* описание переменных */ if(a>=0) b = a; else b = -a; printf("|%d| = %d",a,b); /* печать результата */ } Текст программы включает несколько комментариев, (заключенных между символами /* и */), поясняющих назначение программы и отдельных операторов. Далее включается описание стандартных функций ввода-вывода при помощи команды препроцессору #include. После заголовка функции main() в фигурные скобки заключается тело функции, состоящее из описания переменных (целого типа, с инициализацией переменной a), вычисления значения b и вывода результата расчета. В центральной части программы проверяется знак переменной a; при истинности условия a>=0 выполняется оператор, идущий сразу после проверки условия; при ложности - оператор, идущий после else. Каждая ветвь включает один оператор, поэтому здесь можно обойтись без фигурных скобок. Эта программа составлена в примитивной форме, чтобы только пояснить правила использования конструкции if-else. Программу можно усовершенствовать, во-первых, организовав ввод значения переменной a в режиме диалога, во-вторых, вынеся расчетную часть (вычисление значения b) в отдельную функцию. Последнее позволит использовать эту функцию и в других программах, то есть как стандартную (так применяется стандартная математическая функция abs). Детально указанные операции будут рассмотрены в следующих главах. Пример 7.2. Программа определения коэффициента поперечной устойчивости вагона. Математическая формулировка и блок-схема приведены в примере 1.4. /* Определение коэффициента поперечной устойчивости вагона. */ #include #include void main() { /* Описание переменных и вариант исходных данных */ double T=30000.,Q=60000.,beta=60.,mu=0.25,Pt,N,b_rad,eta; int m=4; /* начало разветвлений: */ if(Q<0.001) { if(m==4) N = 0.5; else N = 1.; } else { if(m==8) N = 3.; else N = 1.; } /* конец разветвлений */ Pt = (T+Q)/2./N; b_rad = beta/57.296; /* перевод угла в радианы */ eta = ( Pt + tan(b_rad) ) / ( Pt - mu * tan(b_rad) ); /* печать результата:*/ printf("Коэффициент поперечной устойчивости: %lf", eta); } Чтобы понять эту программу, надо сопоставить ее текст с блок-схемой алгоритма, показанной на рис. 1.3. Программа начинается с комментария, заключенного в символы /* и */. В начале программы находятся две команды препроцессору #include для включения в текст программы описаний стандартных функций ввода-вывода и математических (находящихся в файлах, соответственно, stdio.h и math.h). Далее идет заголовок функции main(), после которого в фигурных скобках определено тело этой функции, (выделенное отступом). В функции main() сначала описываются переменные и при этом вводятся исходные данные. Обозначения переменных легко понять по математической формулировке и блок-схеме. Единственное отличие состоит в том, что при вычислении тангенса аргумент (угол) должен передаваться функции tan в радианах, поэтому вводится новая переменная b_rad. Можно было бы осуществлять перевод непосредственно в формуле: eta = (Pt + tan(beta/57.296))/(Pt - mu*tan(beta/57.296)); но тогда потребовалось бы на одну операцию больше. "Разветвленная" часть программы начинается с оператора if. Проверяется истинность условия в скобках (порожний ли вагон?); если оно истинно, то выполняется группа операторов, указанная в фигурных скобках непосредственно после проверки условия; если условие ложно, то выполняется группа операторов в фигурных скобках после ключевого слова else. Каждая группа операторов для наглядности выделяется отступом. Само условие (порожний ли вагон?) записано не так, как в блок-схеме. Это связано с тем, что переменная Q (вес груза) представляется как действительное число (double). При сравнении действительных чисел надо учитывать тот факт, что у действительного числа в памяти компьютера могут появляться "лишние" десятичные знаки. Например, введено значение переменной: float Q=0.; а в памяти оно может храниться как 0.00000002. Это может прямо не сказаться на результатах вычислений, но повлияет на проверку условия (Q==0.), то есть покажет, что выгон не порожний. Поэтому в программе целесообразно значение переменной сравнивать со значением, заведомо малым, но отличным от нуля. По физическому смыслу для веса груза Q=0.001 - малое значение, при котором вагон можно считать порожним. Внутри каждой группы, в свою очередь, проверяется значение осности с указанием, как надо вычислять значение переменной N при истинности и при ложности условия. Так как здесь каждая ветвь состоит только из одного оператора, (заканчивающегося символом ";"), эти операторы можно не заключать в фигурные скобки. После всех разветвлений оставшаяся часть алгоритма является линейной: последовательно вычисляются Pt, b_rad, eta и значение переменной eta выводится на экран. Эта программа не является универсальной, потому что она привязана к определенным исходным данным. В дальнейшем мы рассмотрим, как можно вводить данные в процессе выполнения программы (в режиме диалога). Пример 7.3. Программа решения квадратного уравнения. Математическая формулировка: D=b2-4ac; если D>0, то x1,2= ; если D=0, то x1= ; если D<0, то решений нет. Алгоритм. После ввода исходных данных вычисляется значение переменной D. Проверяется условие D>0, в случае истинности которого вычисляются и выводятся значения переменных x1 и x2. В случае ложности производится проверка другого условия (D=0), при выполнении которого вычисляется и печатается значение переменной x1. Последняя ветвь соответствует случаю, когда оба проверявшихся условия не выполняются. Здесь имеется лишь вывод на экран сообщения об отсутствии корней. После этих альтернатив все ветви сходятся. Блок-схема алгоритма: Т екст программы: /*Решение квадратного уравнения*/ #include #include void main() { float a=1.,b=-1.,c=-2.,D,x1,x2; /*описание переменных*/ D = b*b - 4.*a*c; if(D>0) { x1 = ( -b + sqrt(D) )/2./a; x2 = ( -b - sqrt(D) )/2./a; printf("Корни уравнения: %f, %f",x1,x2); } else if(D==0) { x1 = -b /2./a; printf("Корень уравнения: %f",x1); } else printf("Корней нет!"); } 7.3. Условная операция "?" (если?то:иначе) Эта операция является альтернативой применения конструкции if-else, если ее составляющие представляются в виде простых выражений. Приведем ее запись в символическом виде и поясним с помощью блок-схемы. условие?выражение1:выражение2 З начение данной условной операции зависит от выполнения условия. Если оно истинно, то в качестве значения операции принимается значение выражения1, если ложно - то значение выражения2. Пример 7.4. Вычисление модуля целого числа. Математическая формулировка и алгоритм решения задачи разработаны в примере 7.1. /* Определение модуля целого числа. Версия 2 Применение условной операции "если?то:иначе" */ #include void main() { int a=-5, b; /* описание переменных */ b = (a>=0)?a:-a; printf("|%d| = %d",a,b); /* печать результата */ } Пример 7.5. Определение коэффициента поперечной устойчивости вагона. Математическая формулировка и блок-схема приведены в примере 1.4. Предыдущая версия программы с использованием конструкции if-else - в примере 7.2. /* Определение коэффициента поперечной устойчивости вагона. Версия 2. Применение операции "если?то:иначе" */ #include #include void main() { double T=30000.,Q=60000.,beta=60.,mu=0.25,Pt,N,b_rad,eta; int m=4; N = (Q<0.001)?((m==4)?0.5:1.):((m==8)?3.:1.); Pt = (T+Q)/2./N; b_rad = beta/57.296; eta = ( Pt + tan(b_rad) ) / ( Pt - mu * tan(b_rad) ); printf("Коэффициент поперечной устойчивости: %lf", eta); } Эти примеры показывают, что с применением операции "?" программа может быть записана более компактно. Однако имеются ограничения ее применимости. Во-первых, как отмечалось выше, операция применима для простых выражений. Во-вторых, текст программы может оказаться трудным для восприятия (пример 7.5). При выборе того или другого способа реализации алгоритма учитывают различные факторы, в данном случае компактность и наглядность текста. С точки зрения возможности проверки программы, предотвращения и поиска ошибок, предпочтительнее стремиться к большей наглядности. 7.4. Конструкция switch Switch (переключатель) - это еще один вид разветвленной структуры, которая может быть эффективной при множественности путей вычислений в зависимости от проверяемого значения. Пример подобной структуры: s witch(выражение) { case конст1: опер1; опер2; break; case конст2: опер3; break; case конст3: опер4; опер5; break; default: опер6; } Здесь обозначено: switch - ключевое слово, обозначающее операцию; выражение - это любое выражение (или переменная) целого типа; case - ключевое слово для обозначения одной ветви ("случая"); конст1, конст2, конст3 - целые константы; опер1, опер2, ..., опер6 - операторы (заканчиваются точкой с запятой); default - ключевое слово для обозначения ветви "по умолчанию", (если значение выражения не совпало ни с каким случаем из указанных через case); break; - оператор, передающий управление на конец всей структуры, заключенной в фигурные скобки. Вариант default можно не указывать, в этом случае управление "по умолчанию" будет просто передано следующему за конструкцией switch оператору. В процессе выполнения программы при обращении к структуре switch сначала определяется значение целого выражения. Затем это значение последовательно сравнивается со значениями целых констант конст1, конст2, конст3, и в случае совпадения значений управление передается на соответствующую ветвь алгоритма. Если ни одного совпадения не было выявлено, управление передается на ветвь "по умолчанию" (default). Каждое ответвление алгоритма заканчивается оператором break; чтобы передать управление на блок слияния в конце структуры. Если бы этого не было, то конструкция switch работала бы по-иному. Проиллюстрируем это на примере, из сравнения с которым видно назначение оператора break; s witch(выражение) { case конст1: опер1; опер2; case конст2: опер3; case конст3: опер4; опер5; default: опер6; } Из блок-схемы видно, что после выполнения операторов каждой ветви алгоритма будут выполнены и все операторы, указанные после нее. Иногда именно это и требуется, тогда оператор break; не нужен. В любом случае нужно внимательно следить за использованием этого оператора, понимая получающуюся структуру алгоритма. Пример 7.6. Определение коэффициента поперечной устойчивости вагона. Математическая формулировка и блок-схема приведены в примере 1.4; предыдущие версии программы - в примерах 7.2. и 7.5. /* Определение коэффициента поперечной устойчивости вагона. Версия 3. Применение конструкции switch */ #include #include void main() { double T=30000.,Q=60000.,beta=60.,mu=0.25,Pt,N,b_rad,eta; int m=4; if(Q<0.001) /* начало разветвлений: */ switch(m) { case 4: N = 0.5; break; case 6: N = 1.0; break; case 8: N = 1.0; break; default: printf("Ошибка в осности: %d", m); return; } else switch(m) { case 4: N = 1.0; break; case 6: N = 1.0; break; case 8: N = 3.0; break; default: printf("Ошибка в осности: %d", m); return; } /* конец разветвлений */ Pt = (T+Q)/2./N; b_rad = beta/57.296; eta = ( Pt + tan(b_rad) ) / ( Pt - mu * tan(b_rad) ); printf("Коэффициент поперечной устойчивости: %lf", eta); } Первое, что бросается в глаза из сравнения этого варианта программы с предыдущими, это больший объем текста: указание каждого случая осности, варианта по умолчанию (default), введение операторов break. Однако такая конструкция имеет и положительные стороны. Во-первых, представление вариантов значений m здесь более наглядно. Во-вторых, без существенных усложнений можно предусмотреть дополнительные варианты выполнения (введением нового варианта case). Так были явно указаны варианты шестиосного вагона. В-третьих, описание варианта "по умолчанию" позволило задать порядок действий в случае ошибочного ввода осности m. При этом выводится сообщение об ошибке и с помощью оператора return осуществляется выход из функции main() (завершение программы). Подробная проработка различных вариантов является очень полезной, так как она уменьшает вероятность ошибок в процессе работы с программой. Пример 7.7. Простой калькулятор Составим программу, выполняющую одно из четырех арифметических действий, указываемых пользователем. /* Калькулятор */ #include void main() { float a, b, res; char oper; printf("Введите операцию:"); scanf("%f%c%f",&a,&oper,&b); switch(oper) { case '+': res = a + b; break; case '-': res = a - b; break; case '*': res = a * b; break; case '/': res = a / b; break; default: printf("Неверная операция"); } printf("\n%f%c%f=%f",a,oper,b,res); } Для описания знака операции введена переменная oper символьного типа. В этой программе использована стандартная функция scanf для ввода исходных данных с клавиатуры. После запуска программы она будет ожидать, что пользователь введет три значения: первое число (a), знак операции (oper) и второе число (b). Особенности применения функции scanf будут рассмотрены ниже, при описании средств ввода-вывода. Далее производится проверка значения переменной oper, в зависимости от чего выполнение идет по одному из пяти путей. Следует отметить, что проверяется значение символьной переменной, хотя было отмечено, что здесь должно стоять выражение целого типа. Дело в том, что символ может быть однозначно преобразован в целое число. При проверке значения в switch происходит неявное преобразование значения переменной oper к целому типу и это значение сравнивается с "числовыми аналогами" символьных констант '+', '-', '*' и '/'. Остальные особенности реализации данного алгоритма легко видны из сопоставления текста программы с блок-схемой. |