Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Иванова Г.С. - Основы программирования

.pdf
Скачиваний:
2770
Добавлен:
02.04.2015
Размер:
13.53 Mб
Скачать

2. Простейшие конструкции языка

Для корректного выполнения операции присваивания результат выраже­ ния и переменная, записанная в правой части оператора присваивания, должны иметь одинаковые или совместимые типы.

Совместимыми считаются:

все целые типы;

все вещественные типы;

отрезок некоторого базового типа и базовый тип;

два отрезка одного базового типа;

символ и строка.

При несовпадении типов правой и левой частей оператора присваивания для совместимых типов происходит неявное преобразование результата вы­ ражения к типу переменной, указанной в правой части. Например:

Var

L:longint; {переменная типа longint}

Е,х: extended; {переменные типа extended} Linteger; {переменная типа integer} R:real; {переменная типа real}

Begin...

R:=I*E/(x-^L);... {результат выражения, записанного в правой части оператора присваивания, будет иметь тип extended, однако, так как переменная R типа real, то результат будет преобразован в этот тип}

End,

Если типы правой и левой частей оператора присваивания не совмести­ мы, то необходимо использовать явное преобразование типов.

Явное преобразование обычно выполняют посредством использования специальных функций:

Тгипс(х) - преобразует значение вещественного типа в значение целого типа, отбрасывая дробную часть;

Round(x) - преобразует значение вещественного типа в значение целого типа, округляя его до ближайшего целого;

Ord(x) - преобразует значение порядкового типа в его номер;

Cltr(x) - преобразует номер символа по таблице ASCII в сам символ. Например:

а) Varn,nl:integer;xn,xk,h:real; Begin

xn:=I;xk:=5.7; h:=OJ;

n:'=Round((xk'Xn)/h);

{n получит значение 16}

nl:=Trunc((xk'Xn)/h);

... {nlполучит значение 15}

41

Часть 1. Основы алгоритмизации и процедурное программирование

б) Var c:char; x,y:integer: Begin x:=3;

y:= Ord(*A*);{y получит значение 65 - код символа А

по таблице ASCII} с. = Chr(Ord('A*)-^x); ... {с получит значение 'D'}

Кроме того, для явного преобразования типов можно использовать функции, имена которых соответствуют идентификаторам стандартных или определенных пользователем типов. Этот вид преобразования иногда назы­ вают автоопределеиным, например:

Var h'.char;

... h:=Char(65); ... {h получит значение 'А'}

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

Например:

Туре Month=(Jan,FabMcir,ApKMayJun,JulAug,Sep, Oct,Nov,Dec);

Var M:Month;

 

A,B:mteger;

 

C'char;

 

 

L:longint;

 

 

Begin

 

 

A:=10;

C-'E';

 

B:==Integer(C);

{число 69 - код символа E - длиной 2 байта}

М: =Month(A'2);

{значение Sep}

L:=Longint(M);....

{значение 8}

2.6. Процедуры ввода-вывода

Ввод значений. Для ввода значений с клавиатуры используют специаль­ ные процедуры Read и ReadLn (рис. 2.10). Эти процедуры позволяют вводить значения стандартных типов, кроме boolean, и строки (string).

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

42

2. Простейшие конструкции языка

rU Read Vi

 

Идентификатор

 

переменной

L^/ReadLnVI

О

 

Рис. 2.10. Синтаксическая диаграмма <Процедуры ввода с клавиатуры>

Физически операции ввода выполняются с использованием буферОуТ, е. вводимая с клавиатуры последовательность символов сначала помещается в память, а затем, уже из памяти, читается программой. Последовательность передается в буфер ввода по нажатию клавиши Enter. При этом в буфер вме­ сте с кодами введенных символов помещается и код Enter, состоящий из двух символов «#13, #10».

Если ввод осуществляется процедурой ReadLn, то буфер ввода после выполнения операции очищается, причем символы, оставшиеся не обрабо­ танными, игнорируются. Если ввод осуществляется процедурой Read, то очистка не выполняется и, следовательно, следующий оператор ввода начнет читать символы из той же строки. Последнее cyщectвeннo только, если вво­ дятся значения типа char (или string, см. параграф 4.5), так как при вводе чи­

сел пробелы и переход на следующую строку

игнорируются.

Например:

 

 

 

 

а)

Var a,b,c:real;

п:integer;

 

 

 

Begin

 

 

 

 

 

Read(a,b);

{числа могут быть введены в одной строке или в

 

 

 

разных}

 

 

 

ReadLn(c,n);,,. {числа

могут быть введены в той же строке, что и

 

 

 

предыдущие числа}

 

б)

Var a:real;

c:char;

 

 

 

Begin ...

 

 

 

 

 

Read(a); ...

 

 

 

 

Write('Продолжить?

(y/n)

*);

 

Read(c);

{приводит к тому, что после запроса компьютер не

 

переходит в ожидание ввода, как мы предполагали, а вводит

 

следующий символ из буфера ввода, т.е. символ #13 (рис. 2.11)}

Чтобы избежать «игнорирования ввода», необходимо для выполнения предыдущей операции ввода использовать вместо процедуры Read процеду­ ру ReadLn:

43

Часть L Основы алгоритмизации и процедурное программирование

Местоположение указателя ввода перед

чтением символа

Число а

гл/

8 5 #13 #10

Рис. 2.11. Ситуация «игнорирования» ввода

Var a:real; c:char; Begin...

ReadLn(a);... {очистим буфер ввода после выполнения операции}

Write('Продолэюитъ? (у/п) *);

Read(c); {в данном случае все в порядке: после вывода запроса программа ожидает ввода символа}

Вывод значений. Для вывода значений на экран используют процедуры Write и WriteLn (рис. 2.12). Эти процедуры предназначены для вывода значе­ ний стандартных типов и строк.

Целочисленный литерал <Целое 1> интерпретируется как ширина поля, в которое выводится значение, причем выводимые значения прижимаются к правой границе. Если указанной ширины поля недостаточно, то она автома­ тически увеличивается до нужного значения. Если <Целое 1> не указано, то его значение определяется количеством выводимых символов.

Целочисленный литерал <Целое 2> указывается только для веществен­ ных чисел: он определяет количество цифр дробной части числа. Если <Целое 2> указано равным О, то ни дробная часть числа, ни десятичная точка не выводятся. Если <Целое1> и <Целое 2> не указаны, то веш[ественные числа выводятся в виде мантиссы и порядка, причем ширина поля вывода по умол­ чанию принимается равной 23, а количество дробных цифр - 14.

Логические значения выводятся как TRUE или FALSE.

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

г*^ Write \h0^Выражение

Ч1ЯЦелое1

Г0-

U/writeLn\j

<1ЯЦелое2 И

Рис. 2.12. Синтаксическая диаграмма <Процедуры вывода на экран>

44

2. Простейшие конструкции языка

После вывода значений процедурой WriteLn курсор переводится на сле­ дующую строку.

Пример 2.1. Разработать программу вычисления корней уравнения Ах2-1-Вх+С=0 при условии, что дискриминант - неотрицательное число.

Формула корней уравнения известна:

-b±Vd

х,2 =

'

 

где d = Ь^ - 4ас.

Алгоритм программы выглядит следующим образом:

Корни уравнения:

Ввести а, Ь, с

d:= b^ - 4ас

е:= Ь / (2а)

X, := - е + V d / (2а) Х2 := - е - V^d / (2а) Вывести Х|, Х2

Конец алгоритма.

Ниже приведен текст программы.

Program ex;

Var a,bx,xl,x2,e,d:real; {описываем переменные} Begin

WriteLnCВведите коэффициенты уравнения: '); ReadLn(a,b,c); {вводим параметры} d:=b*b'4*a*c; {определяем дискриминант}

е:=Ь/(2*а); {определяем значение вспомогательной переменной} xl:=-e^sqrt(d)/(2'^a): {определяем xj}

x2:^'e'Sqrt(d)/(2^a); {определяем Х2}

WriteLn('xl= \х1:6:2, *х2= \х2:б:2); {выводим результаты} End

2.7. Практикум. Оценка точности результатов

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

Пусть «А» - точное значение числа, а «а» - его приближенное представ­ ление, тогда ошибкой или абсолютной погрешностью приближенного пред­ ставления числа А называют значение

Л = |А - а|.

45

Часть 1. Основы алгоритмизации и процедурное программирование

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

Аа>А =|А-а|.

Абсолютная погрешность не является единственной характеристикой ошибки. Сравним два варианта результата с одинаковой погрешностью: 100±1 и 1±1. Очевидно, что с точки зрения практики необходимо иметь ха­ рактеристику, позволяющую при оценке ошибки учитывать само значение. Такой характеристикой является относительная погрешность.

Относительной погрешностью называют отношение абсолютной по­ грешности числа к его модулю (А;»^0):

5 = А /А.

С учетом того, что точное значение А обычно не известно, в качестве

предельной относительной погрешности или «оценки сверху» относитель­ ной погрешности можно использовать значение

5, = Д,/(а-Д,).

Погрешность результата вычислений складывается из погрешностей:

допущенных при постановке задачи за счет ее упрощения (погрешно­ сти задачи);

связанных с использованием приближенных методов решения задачи (погрешности метода);

связанных с использованием приближенных значений параметров, на­ пример, любых физических констант (начальные погрешности);

связанных с ограниченным количеством разрядов, используемых для представления чисел (погрешности округления);

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

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

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

Пример 2.2. Выполнить оценку погрешности представления числа 1/3 и вычислений над числами типа real.

46

2. Простейшие конструкции языка

Program ex;

Var у,у1у2,уЗ,у4,у5,у6,р:геа1; Begin

y-l; yl-y/3;

WriteLnCyl = ',yl:16:14); {выводит yl=0.33333333333348} y2:=sqrt(yl};

y3:=sqr(y2);

у4:=уЗ/14;

у5:=у4*]4;

WriteLnCy6^ \у6:16:14): {выводит у6=1.00000000000182} WriteLn(y = \y:16:14); {выводит у =1.06000000000000}

End

Откуда погрешности представления числа 1/3 в формате real:

А,/3 = |1/3 - у1| = I 0.33333333333333 - 0.333333333333481 = 0.15-10-'2,

5,/з = 0.15 10-12/(1/3) = 0.45-10-10

а погрешность выполнения операций над числами, представленными в формате типа real, в конкретном случае:

Дуб = 1У - Уб1 = 11 - 1.00000000000182| = 0.182.1 о-1', 5уб = Дуб = 0.182.10-11/1= 0.182.10-11.

Пример 2.3. Из математики известно, что ch^ х - sh^ х = 1. Разработать программу, «проверяющую» это равенство.

Наша программа должна вводить значение х и для него считать

у, = (ех +е-х)/2, У2 = (е>^-е-^)/2,

У = У1^-У2^-

Полученные значения У], У2 и у выведем на экран.

Program ex;

Var X, у, yly y2:real; Begin

Write('Введите значение x: *); ReadLn(x);

yl: ='(exp(x)-^exp('X))/2;

47

Часть 1. Основы алгоритмизации и процедурное программирование

у2:=(ехр(х) - ехр('Х))/2; y:=^sqr(yl) - sqr(y2): WriteLnCyl^\yl:13:ll): WnteLn(y2=\y2:13:ll); WriteLn(y=^\y:13:ll);

End.

Последовательно вводя x = 5, 6, 7, ..., 14, получаем, что у = 1, хотя по­ грешность результата растет. Однако при х=15 у=0 (!!!). Попробуем понять, почему такое происходит, для чего сведем результаты при х = О, 5, 10, 14, 15, 20 в табл. 2.4 и рассчитаем абсолютную и относительную погрешности ре­ зультата. Из таблицы видно, что значения функций ch х и sh х с увеличени­ ем X быстро растут. А чем больше число, тем длиннее запись его мантиссы. И, наконец, при х=15 разрядной сетки для записи мантиссы числа перестает хватать. При этом младшие разряды мантиссы, которые различны для ch х и sh X, отбрасываются, и при возведении чисел в квадрат мы получаем одина­ ковые результаты.

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

ch^ X - sh^ X = (ch X - sh x)(ch x + sh x),

в котором значения уменьшаемого и вычитаемого растут не так быстро.

 

 

 

Т а б л и ц а 2.4

X

У1,У2

У

А, 5

0

у1=1.00000000000

1.00000000000

А=0,

 

у2=0.00000000000

6=0%

 

 

5

у 1=74.2099485248

0.99999999989

Л=0.00000000011

 

у2=74.2032105778

5=0.000000011%

 

 

10

у 1=11013.2329201

1.00008608813

А=0.00008608813

 

у2=11013.2328747

 

6=0. 008608813 %

14

у 1=601302.142083

1.14689281583

А=0.14689281583

 

у2=601302.142082

 

6=14.689281583%

15

у 1=1634508.68623

0.00000000000

А=1

 

у2= 1634508.68623

 

6=100 %

20

у 1=242582597.704

0.00000000000

А=1

 

у2=242582597.704

 

6=100 %

48

2 Простейшие конструкции языка

Тема оценки погрешностей вычислений будет продолжена в параграфе

3.5.

Задания для самопроверки

Задание 1. Измените в программе примера 2.3 тип х на double. Объясните полученные результаты.

Задание 2. Разработайте программу, которая «проверяет» формулу sin^ X + cos2x= 1.

Убедитесь, что при любых допустимых значениях х мы получаем правильные результаты. Почему?

3. УПРАВЛЯЮЩИЕ ОПЕРАТОРЫ ЯЗЫКА

Программы, содержащие в разделе операторов только операторы ввода-вывода и операторы присваивания, выполняются последовательно оператор за оператором. Такие программы ндзыътогт линейными, они реализуют линейный процесс вычислений. Для ор­ ганизации разветвленных и циклических процессов вычислений используют управляю­ щие операторы языка, определяющие последовательность выполнения операторов про­ граммы. В данной главе мы рассмотрим управляющие операторы языка Borland Pascal, к которым относятг опсратор условной передачи управления, оператор выбора, операторы организации циклов, а также неструктурные операторы и процедуры передачи управле­ ния.

3.1. Оператор условной передачи управления

Оператор условной передачи управления (рис. 3.1) используют для про­ граммирования ветвлений, т. е. ситуаций, когда возникает необходимость при выполнении условия реализовывать одни действия, а при нарушении - другие. Условие записывают в виде логического выражения, в зависимости от результата которого осуществляется выбор одной из ветвей: если резуль­ тат true, то выполняется оператор, следующий за служебным словом then, иначе - оператор, следующий за служебным словом else.

В каждой ветви допускается запись одного оператора (в том числе и дру­ гого jf) или составного оператора.

Составным оператором в Borland Pascal называют последовательность операторов, заключенную в операторные скобки begin...end. Операторы по­ следовательности отделяют друг от друга точкой с запятой «;». Перед end точку с запятой можно не ставить. Перед else точка с запятой не ставится никогда, так как в этом случае запись условного оператора продолжается.

if

Логическое

then

Оператор

Оператор г

выражение

 

 

 

 

 

 

else

Рис. 3.1. Синтаксическая диаграмма <Оператор условной передачи управления>

50