Nickolay.info. Обучение. Учебник по Паскалю. Глава 7

7. Разветвляющийся вычислительный процесс и условный оператор

 

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

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

 

7.1. Логические выражения

 

Логические выражения (ЛВ) строятся из АВ, операций отношения, логических операций и круглых скобок.

Результатом вычисления ЛВ является одно из двух значений: true или false.

 

7.2. Операции отношения

 

Операции отношения (сравнения) имеют следующий общий вид:

АВ1 ОО АВ2

где АВ -- арифметические выражения, ОО -- один из следующих знаков операций:

< <=   >    >=   =    <>

Последний знак обозначает отношение "не равно". Обратите также внимание на запись отношений "меньше или равно", "больше или равно".

В любое логическое выражение должна входить хотя бы одна операция отношения.

Приведем примеры ЛВ, включающих одну ОО:

d<0            -- выбор ветви вычислений зависит от значения d;

sqr(x)+sqr(y)<=sqr(r) -- результат будет равен true для точек с координатами (x, y), лежащих внутри круга радиуса R с центром в начале координат;

cos(x)>1 -- результат этого ЛВ всегда равен false.

К вещественным значениям в общем случае неприменима операция = ("равно") из-за неточного представления этих значений в памяти компьютера. Поэтому для вещественных переменных отношение вида a=b часто заменяется на abs(a-b)<eps, где eps -- малая величина, определяющая допустимую погрешность.

 

7.3. Логические операции

 

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

 

Табл. 7.1. Логические операции языка Паскаль

Математическая запись

Запись на Паскале

Название

not

Отрицание

and

Операция "И" (логическое умножение)

or

Операция "ИЛИ" (логическое сложение)

xor

Операция "исключающее ИЛИ"

 

Операция NOT применима к одному логическому выражению (является унарной). Ее результат равен true, если выражение ложно и наоборот.

Например, выражение NOT (sin(x)>1) всегда даст значение true.

Операция AND связывает не менее двух логических выражения (является бинарной). Ее результат равен true, если все выражения истинны или false, если хотя бы одно из выражений ложно.

В качестве примера распишем выражение . Т. к. операции принадлежности в Паскале нет, используем операцию AND и операции отношения: (x>=a) and (x<=b).

Математическое выражение a,b,c>0 (одновременно) будет иметь вид (a>0) and (b>0) and (c>0).

Операция OR также связывает не менее двух логических выражений. Ее результат равен true, если хотя бы одно выражение истинно и false, если все выражения ложны.

Распишем выражение . На Паскале оно будет иметь вид(x<a) or (x>b). Другой способ связан с применением операции NOT: not ((x>=a) and (x<=b)).

Условие "хотя бы одно из значений a,b,c положительно" может быть записано в виде (a>0) or (b>0) or (c>0) .

Условие "только одно из значений a,b,c положительно" потребует объединения возможностей операций AND и OR:

(a>0) and (b<=0) and (c<=0) or

(a<=0) and (b>0) and (c<=0) or

(a<=0) and (b<=0) and (c>0).

Операция XOR, в отличие от OR, возвращает значение "ложь" (false) и в том случае, когда все связанные ей логические выражения истинны. Чтобы лучше уяснить это отличие, составим так называемую таблицу истинности двух логических операций (табл. 7.2). Для краткости значение false обозначим нулем, а true -- единицей. Для двух логических аргументов возможно всего 4 комбинации значений 0 и 1.

 

Табл. 7.2. Таблица истинности операций OR и XOR

Аргумент A

Аргумент B

A or B

A xor B

0

0

0

0

0

1

1

1

1

0

1

1

1

1

1

0

 

В качестве примера использования операции XOR запишем условие "только одно из значений a,b положительно":

(a>0) xor (b>0).

К сожалению, записать условие "только одно из значений a,b,c положительно" в напрашивающемся виде (a>0) xor (b>0) xor (c>0) нельзя -- результат этого выражения будет равен true и в случае, когда все три значения положительны. Связано это с тем, что при последовательном расчете логических выражений слева направо (1 xor 1) xor 1 будет равно 0 xor 1 = 1.

С помощью xor удобно организовывать различного рода переключатели, которые последовательно должны принимать одно из двух состояний:

x := x xor true; writeln ('x=', x);

x := x xor true; writeln ('x=', x);

Независимо от начального значения логической переменной x, второе выведенное на экран значение будет логическим отрицанием первого. В реальной практике конструкции подобные x := x xor true; не дублируются в коде многократно, а применяются внутри цикла (см. гл. 9).

Приоритет логических операций следующий:  самая старшая операция -- not, затем and, следующие по приоритету -- or и xor (равноправны между собой), самый низкий приоритет имеют операции отношения. Последнее служит причиной того, что в составных условиях отдельные отношения необходимо заключать в круглые скобки, как и сделано во всех примерах раздела.

 

7.4. Короткий условный оператор

 

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

if логическое_выражение then оператор1;

Сначала вычисляется логическое выражение, если оно имеет значение true, то выполняется оператор1, иначе оператор1 игнорируется. Блок-схема соответствующего вычислительного процесса представлена на рис. 7.1.

Рис. 7.1. Блок-схема короткого условного оператора

 

Если по условию требуется выполнить несколько операторов, их необходимо заключить в операторные скобки begin...end;, образуя единый составной оператор:

if d>0 then begin

     x1:=(-b+sqrt(d))/(2*a);

     x2:=(-b-sqrt(d))/(2*a);

     writeln (x1:8:3,x2:8:3);

end;

Здесь по условию d>0 выполняется 3 оператора, первые два из которых вычисляют корни x1 и x2 квадратного уравнения, а последний выводит на экран найденные значения корней.

Следующий пример иллюстрирует поиск значения y=max(a,b,c). Поскольку стандартной функции для нахождения максимума в Паскале нет, применим 2 коротких условных оператора:

y:=a;

if b>y then y:=b;

if c>y then y:=c;

Вообще, для условной обработки N значений требуется N-1 короткий условный оператор.

 

7.5. Полный условный оператор

 

Эта форма условного оператора позволяет запрограммировать 2 ветви вычислений. Общий вид полного условного оператора следующий:

if логическое_выражение then оператор1

else оператор2;

Оператор работает следующим образом: если логическое выражение имеет значение true, то выполняется оператор1, иначе выполняется оператор2. Всегда выполняется только один из двух операторов. Перед ключевым словом else ("иначе") точка с запятой не ставится, т.к. if-then-else -- единый оператор.

Вычислим значение m=min(x,y) с помощью полного условного оператора:

if x<y then m:=x else m:=y;

В следующем примере выполним обработку двух переменных: если значения a и b одного знака, найти их произведение, иначе заменить нулями.

if a*b>0 then c:=a*b

else begin

  a:=0; b:=0;

end;

Из примера видно, что к ветви алгоритма после ключевого слова else, состоящей более чем из одного оператора, также применяются операторные скобки.

 

7.6. Составной условный оператор

 

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

if логическое_выражение1 then оператор1

else if логическое_выражение2 then оператор2

...

else if логическое_выражениеN then операторN

else оператор0;

При использовании оператора последовательно проверяются логические выражения 1, 2, ... ,N, если некоторое выражение истинно, то выполняется соответствующий оператор и управление передается на оператор, следующий за условным. Если все условия ложны, выполняется оператор0 (если он задан). Число ветвей N неограниченно, ветви else оператор0; может и не быть.

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

var x:real;

begin

 write ('Введите x:'); readln (x);

 if x<0 then writeln ('Отрицательный')

 else if x=0 then writeln ('Ноль')

 else if abs(x)<1 then

  writeln ('По модулю меньше 1')

 else writeln ('Больше 1');

end.

Условие x<0 сработает, например, для значения x=-0.5, что не позволит программе проверить условие abs(x)<1.

Еще одну распространенную ошибку работы с составным условным оператором показывает произведенный ниже расчет знака n переменной a:

if a<0 then n:=-1;

if a=0 then n:=0

else n:=1;

Применение одного короткого и одного полного условных операторов является здесь грубой ошибкой -- ведь после завершения короткого условного оператора для всех ненулевых значений a будет выполнено присваивание n:=1. Правильных вариантов этого расчета, по меньше мере, два:

if a<0 then n:=-1;

if a=0 then n:=0;

if a>0 then n:=1;

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

if a<0 then n:=-1;

else if a>0 then n:=1;

else n:=0;

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

Можно сделать вывод, что при программировании многовариантных расчетов следует соблюдать осторожность, чтобы не допустить "потерянных" или неверно срабатывающих ветвей алгоритма.

В качестве еще одного примера рассчитаем значение функции, заданной графически (рис. 7.2).

Рис. 7.2. Функция, заданная графически

 

Перепишем функцию в аналитическом виде:

Одним из вариантов запрограммировать вычисление y(x) мог бы быть следующий:

if abs(x)>1 then y:=0

else if x<0 then y:=x+1

else y:=1-x;

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

if x<-1 then y:=0

else if x<0 then y:=x+1

else if x<1 then y:=1-x

else y:=0;

7.7. Вложенные условные операторы

 

Когда после ключевых слов then или else вновь используются условные операторы, они называются вложенными. Число вложений может быть произвольно, при этом действует правило: else всегда относится к ближайшему оператору if , для которого ветка else еще не указана. Часто вложением условных операторов можно заменить использование составного.

В качестве примера рассмотрим программу для определения номера координатной четверти p, в которой находится точка с координатами (x,y). Для простоты примем, что точка не лежит на осях координат. Без использования вложений основная часть программы может иметь следующий вид:

if (x>0) and (y>0) then p:=1

else if (x<0) and (y>0) then p:=2

else if (x<0) and (y<0) then p:=3

else p:=4;

Однако использование такого количества условий представляется явно избыточным. Перепишем программу, используя тот факт, что по каждое из условий x>0, x<0 оставляет в качестве значения p только по 2 возможных четверти из 4:

if x>0 then begin

 if y>0 then p:=1

 else p:=4;

end

else begin

 if y>0 then p:=2

 else p:=3;

end;

В первом фрагменте программе проверяется от 2 до 6 условий, во втором -- всегда только 2 условия. Здесь использование вложений дало существенный выигрыш в производительности.

Рассмотренный в п. 7.6 пример с определением знака числа может быть переписан и с использованием вложения:

if a>0 then n:=1

else begin

 if a<0 then n:=-1

 else n:=0;

end;

Однако, как эти операторы, так и составной условный оператор из п. 7.6 проверяют не более 2 условий, так что способы примерно равноценны.

 

7.8. Оператор выбора

 

Для случаев, когда требуется выбор одного значения из конечного набора вариантов, оператор if удобнее заменять оператором выбора (переключателем) case:

case выражение of

 список1: оператор1;

 список2: оператор2;

 . . .

 списокN: операторN;

 else оператор0;

end;

Оператор выполняется так же, как составной условный оператор.

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

Мин.значение .. Макс.значение

Оператор диапазона записывается как два рядом стоящих символа точки. В диапазон входят все значения от минимального до максимального включительно.

В качестве примера по номеру месяца m определим число дней d в нем:

case m of

 1,3,5,7..8,10,12: d:=31;

 2: d:=28;

 4,6,9,11: d:=30;

end;

Следующий оператор по заданному символу c определяет, к какой группе символов он относится:

case c of

 'A'..'Z','a'..'z':

   writeln ('Латинская буква');

 'А'..'Я','а'..'п','р'..'я':

   writeln ('Русская буква');

 '0'..'9':

   writeln ('Цифра');

 else writeln ('Другой символ');

end;

Здесь отдельные диапазоны для русских букв от "а" до "п" и от "р" до "я" связаны с тем, что между "п" и "р" в кодовой таблице DOS находится ряд не-буквенных символов (см. Приложение 1).

Если по ветви оператора case нужно выполнить несколько операторов, действует то же правило, что для оператора if, т. е. ветвь алгоритма заключается в операторные скобки begin ... end;.

7.9. Примеры программ с условным оператором

 

Приведем несколько примеров законченных программ, использующих РВП.

1. Проверить, может ли быть построен прямоугольный треугольник по длинам сторон a, b, c.

Проблема с решением этой задачи -- не в проверке условия теоремы Пифагора, а в том, что в условии не сказано, какая из сторон может быть гипотенузой. Подходов возможно несколько -- запрашивать у пользователя ввод данных по возрастанию длины сторон, проверять все три возможных условия теоремы Пифагора и т. п. Используем наиболее естественное решение -- перед проверкой условия теоремы Пифагора упорядочим величины a, b, c так, чтобы выполнялись соотношения abc. Для этого применим прием с обменом значений переменных из п. 4.1.

var a,b,c, {Длины сторон}

 s:real;{Буферная переменная для обмена}

begin

{ Секция ввода данных }

 writeln;

 write ('Введите длину 1 стороны:');

 readln (a);

 write ('Введите длину 2 стороны:');

 readln (b);

 write ('Введите длину 3 стороны:');

 readln (c);

{ Сортируем стороны по неубыванию }

 if (a>b) then begin

    s:=a; a:=b; b:=s;

 end;

 if (a>c) then begin

    s:=a; a:=c; c:=s;

 end;

 if (b>c) then begin

    s:=b; b:=c; c:=s;

 end;

{ Проверка и вывод }

 if abs(a*a+b*b-c*c)<1e-8 then writeln

  ('Прямоугольный треугольник ',

    'может быть построен!')

 else writeln('Прямоугольный треугольник ',

    'не может быть построен!')

end.

 

2. Определить, попадает ли точка плоскости, заданная координатами (a, b) в прямоугольник, заданный координатами двух углов (x1, y1) и (x2, y2).

Как и в предыдущей задаче, было бы не совсем корректно требовать от пользователя вводить данные в определенном порядке -- гораздо лучше при необходимости поменять x- и y-координаты прямоугольника так, чтобы пара переменных  (x1, y1) содержала координаты левого нижнего угла прямоугольника, а (x2, y2) -- правого верхнего.

var x1,y1,x2,y2,a,b:real;

begin

 writeln ('Введите координаты 1 угла:');

 read (x1,y1);

 writeln ('Введите координаты 2 угла:');

 read (x2,y2);

 if x1>x2 then begin

  a:=x1; x1:=x2; x2:=a;

 end;

 if y1>y2 then begin

  a:=y1; y1:=y2; y2:=a;

 end;

 writeln ('Введите координаты точки:');

 read (a,b);

 if (x1<=a) and (a<=x2)

    and (y1<=b) and (b<=y2) then writeln

   ('Точка попадает в прямоугольник')

 else writeln

   ('Точка не попадает в прямоугольник');

end.

 

3. Вводится денежная сумма в рублях и копейках. Программа печатает введенную сумму с правильной формой слов "рубли" и "копейки", например, "123 рубля 15 копеек".

Окончание, используемое для слов "рубли" и "копейки", зависит от последней цифры суммы, которую можно получить, взяв остаток от деления на 10 (1058 рублей, 38 рублей и т.д.). Исключения -- суммы с последними двумя цифрами от 11 до 19 включительно, которые всегда произносятся "рублей" и "копеек" (511 рублей, но 51 рубль). Используя эту информацию, составим программу.

var r,k,o10,o100:integer;

begin

 writeln;

 write ('Введите количество рублей, ',

     'затем пробел и количество копеек:');

 read (r,k);

 writeln;

 o10:=r mod 10;   {Взяли последнюю цифру}

 o100:=r mod 100; {...и 2 последних цифры}

 write ('Правильно сказать: ',r,' ');

 {Печатаем число рублей, затем пробел}

 if (o100>10) and (o100<20)

     or (o10>4) or (o10=0) then

  write ('рублей')

 else if (o10>1) and (o10<5) then

  write ('рубля')

 else

  write ('рубль');

 {аналогично для копеек:}

 o10:=k mod 10;

 o100:=k mod 100;

 write (' ',k,' ');

 {печатаем число копеек с пробелами}

 if (o100>10) and (o100<20) or

     (o10>4) or (o10=0) then

  write ('копеек')

 else if (o10>1) and (o10<5) then

  write ('копейки')

 else write ('копейка');

end.

Рейтинг@Mail.ru
вверх гостевая; E-mail