Файл: Основы программирования на языке Pascal ( Ссылочные типы в языке программирования Pascal. Создание ссылочных типов ).pdf
Добавлен: 01.04.2023
Просмотров: 101
Скачиваний: 1
117 124 82+92+102+…+1232 +1242
При i=125 происходит выход из цикла и переход к оператору writeln ('S=' ,S);
Эта, казалось бы, правильно написанная программа дает результат S=-12250. Как может получиться отрицательный результат при суммировании квадратов – заведомо положительных чисел? Такой результат мы получаем путем суммирования и занесения результатов в ячейку S типа integer, а в integer могут быть помещены числа в диапазоне -32768 +32767, поэтому после переполнения ячейки памяти в знаковый разряд переносится 1, которая интерпретируется машиной как '-'. Чтобы этого избежать, можно описать S как longint. Тогда результат работы программы будет правильный и равен 643110.
Program Prim12a; Var i,s:longint;
BEGIN s:=0;
for i:= 8 to 124 do
s:=s+sqr(i);
writeln('s=',s);
readln; END.
Можно также описать S как real. Program Prim12b; Var i:integer; s:real;
BEGIN s:=0;
for i:= 8 to 124 do
s:=s+sqr(i);
writeln('s=',s);
readln;
END.
Результат работы этой программы будет равен 6.4311000000E+05, что то же самое, что и 643110, если считать, что запись 6.4311000000E+05 равносильна математической записи 6,431110+5.
Правила применения оператора цикла for
1.Тело цикла - всегда один оператор. Если нужно, чтобы тело цикла состояло из нескольких операторов, то их объединяют в один с помощью операторных скобок begin, end.
for i:=l to n do
Begin S:=S+sin(x)/cos(x);
x=x+0.01;
writeln('S=' ,S)
end.
Операторы, взятые в операторные скобки begin, end, считаются одним составным оператором.
2. Начальное и конечное значения параметра цикла в общем случае являются выражениями. Тип этих выражений и тип параметра цикла должны совпадать. Здесь допускается применение любых типов, которые упорядочены с каким-либо фиксированным шагом, это может быть любой скалярный тип, кроме вещественного, т.е. байтовый, целый, символьный, перечисляемый, интервальный, булевский. на практике чаще всего применяют целочисленный тип.
3. В теле цикла параметр цикла не должен изменяться.
4. Нельзя с помощью оператора перехода goto войти в тело цикла, минуя заголовок.
5. Выйти из тела цикла в программу можно по if..goto, не дожидаясь полного перебора параметров цикла.
Пример: написать программу, выводящую буквы латинского алфавита в одну сторону, с пробелом между ними.
Применяем здесь в качестве параметра цикла символьную переменную, в качестве начального значения параметра – константу 'A', конечного – 'Z'.
Program Prim13;
Var
i:char;
BEGIN
for i:='a' to 'z' do
write(' ',i);
readln; END.
Пример: вычислить значение определенного интеграла на участке a,b для функции cos х, т.е. определить площадь, заштрихованную на рис. 4.1.
Х
b
а
Рис. 4.1
0
Y
Применим метод трапеций, суть которого заключается в том, что область делится на ряд трапеций, площади которых вычисляются и суммируются. Чем на большее количество трапеций мы разделим область, тем точнее получим результат (см.рис.4.2).
Х
b
а
Рис. 4.2
0
Y
Program Prim14;
Var i,n:integer; a,b,x,h,s:real;
BEGIN writeln(' введите к-во точек разбиения n и величины а, b');
readln(n,a,b);
h:=(b-a)/n; s:=0; x:=a;
for i:=1 to n do
Begin s:=s+(abs(cos(x))+abs(cos(x+h)))/2*h;
x:=x+h; end;
writeln('s=',s);
readln; END.
Замечание: при вычислении S (так как функция пересекает ось OX) часть площадей может иметь отрицательное значение, поэтому мы берем abs(f(x)).
Усложним задачу: пусть нужно найти площадь криволинейной трапеции (интеграл), где функция от точки а до b sin(x), а после b до с cos(x).
Program Prim15;
Var i,n:integer; a,b,c,x,h,s,fxpred,fxposl:real;
BEGIN writeln('введите к-во точек разбиения n и величины а, b, c');
readln(n,a,b,c);
h:=(c-a)/n; s:=0; x:=a;
for i:=1 to n do
Begin
{ определимся, по какой из функций считать стороны трапеций }
if (x>=a) and (x<=b) then fxpred:= abs(sin(x)) else
fxpred:=abs(cos(x));
if (x+h>=a) and (x+h<=b) then fxposl:= abs(sin(x+h)) else
fxposl:=abs(cos(x+h));
s:=s+(fxpred+fxposl)/2*h;
x:=x+h; end;
writeln('s=',s);
readln; END.
В последних двух программах мы заменяем площадь криволинейной трапеции (интеграл) суммой трапеций. При этом нам неизвестно, какую ошибку мы допустим; знаем, что чем больше точек разбиения, тем точнее будет результат. Поставим задачу: совершенно точно знать, что мы посчитаем интеграл с точностью не менее заданной, например, что разность между площадью криволинейной трапеции и суммой прямоугольных трапеций будет не больше Е = 0.01. Для этого нам нужно посчитать результат для количества точек, например 10, затем – 100. Сравнить величины площадей, и, если их разность будет меньше Е, то можно с уверенностью сказать, что площадь для разбиения на 100 интервалов будет найдена с точностью не менее Е. Если разность между расчетами на 10 и 100 интервалов больше Е, то следует разбить область интегрирования на 1000 интервалов и сравнить сумму для 100 интервалов с суммой для 1000 интервалов, и если их разница будет меньше Е, то за результат принять площадь, посчитанную для 1000 разбиений и т.д. рано или поздно мы получим искомый результат. Сравнения сумм площадей трапеций будем производить с помощью оператора if.
Пусть функция будет cos(x). Напишем программу:
Program Prim16;
Label NAH,KON;
Var i,n:integer; a,b,x,h,spred,spos,e:real;
BEGIN writeln('введите точность е и границы a, b');
readln(e,a,b); spred:=9.9e+10; h:=0; n:=10;
NAH: spos:=0; h:=(b-a)/n; x:=a;
for i:=1 to n do
Begin spos:=spos+(abs(cos(x))+abs(cos(x+h)))/2*h;
x:=x+h; end;
if abs(spos-spred)<=e then Begin
writeln('s=',spos,' n=',n); goto KON; end
else spred:=spos; n:=n*10; x:=a; goto nah;
KON: readln; END.
Работа программы: for i: = i to n do, в первый раз оператор
Spоs: = spos+(abs(cos(x))+abs(cos(x+h)))/2h
посчитает значение интеграла для 10 точек разбиения. Затем if сравнит полученное значение spos с 9.9Е+10. Конечно, разница между этими величинами не будет меньше е, тогда мы забудем число 9.9Е+10, так как присвоим spred: = spоs, чтобы при следующем выполнении if сравнивать предыдущее и последующее значения интеграла. После этого увеличим количество точек разбиения n: = n10 и вернемся на начало вычисления spos, т.е. интеграла для 100 интервалов goto nach. После нового вычисления spos сравним 100 интервалов для 10 интервалов spred. Если разность между ними не меньше Е, то забудем значение s для 10 интервалов и занесем в spred значение для 100 интервалов. Затем вычислим значение интеграла для разбиения 1000 и результат занесем в spos, сравним spred и spos и т.д.
4.2. Оператор repeat. Общий вид
В отличие от for, оператор repeat (а также while) применяют тогда, когда неизвестно точно, сколько раз будет выполняться тело цикла. Общий вид оператора
repeat
оператор;
оператор;
– – – – – – – –
оператор until булевское выражение;
Работа оператора. Сначала выполняются операторы, составляющие тело цикла, затем выполняется булевское выражение, и если оно ложно, вновь выполняется тело цикла. Выход из цикла происходит, когда булевское выражение станет истинным.
Явного перебора параметров в repeat не предусмотрено, поэтому изменение параметров осуществляется в теле цикла. Здесь же мы должны позаботиться , чтобы рано или поздно булевское выражение стало истиной.
Замечания: 1.Тело оператора repeat выполняется по меньшей мере один раз. Оператор repeat еще называют циклом с послеусловием.
2. Перед until ';' не ставится.
Пример: найти сумму четных чисел в интервале от 0 до 100.
Program Prim17;
Var i,s:integer;
BEGIN i:=0; s:=0;
repeat
i:=i+2; s:=s+i
until i>100;
writeln('s=',s);
readln; END.
Пример: найти ток в цепи переменного тока в зависимости от частоты тока. Известно, что при последовательном соединении активной, емкостной и индукционной нагрузок сила тока может быть вычислена по формуле
Пусть U = 220, R = 100, L = 0.57, С = 3.210-3, Fначальное=10; F будем менять с шагом 0.5. Печатать все результаты. Результаты, когда i3, нас не интересуют.
Program Prim18;
Var i,r,f,l,c,u:real;
BEGIN writeln(' введите значения u r l c');
readln(u,r,l,c);
f:=50;
repeat i:=u/sqrt(sqr(r)+sqr(2*pi*f*l-1/(2*pi*f*c)));
f:=f-1;
writeln('f=',f,' i=',i)
until i>3;
readln; END.
4.3. Оператор While
Оператор While цикла с предусловием.
While – булевское выражение; do – тело цикла.
Работа оператора. Вначале вычисляется булевское выражение, и если оно истинно, то выполняется тело цикла; если оно ложно, то происходит выход из цикла.
Тело цикла – один оператор, как правило, составной. Мы должны позаботиться о том, чтобы булевское выражение в теле цикла на каком-то этапе стало ложным, иначе цикл никогда не закончится. Цикл можно сделать умышленно бесконечным, написав: while true do оператор;
Мы будем применять эту конфигурацию при написании программ обработки файлов.
Пример: найти сумму четных чисел в интервале от 0 до 100.
Program Prim19;
Var i,s:integer;
BEGIN i:=0; s:=0;
while i<100 do
Begin i:=i+2; s:=s+i; end;
writeln('s=',s);
readln; END.
Пример: в цепи переменного тока найти ток в зависимости от его частоты.
Program Prim20;
Var i,r,f,l,c,u:real;
BEGIN writeln('введите значения u,r,l,c');
readln(u,r,l,c);
f:=50;
while i<3 do
Begin i:=u/sqrt(sqr(r)+sqr(2*pi*f*l-1/(2*pi*f*c)));
f:=f-1;
writeln('f=',f,' i=',i); end;
writeln('i>3, i=',i);
readln; END.
4.4. Вложенные циклы
Поскольку тело любого цикла состоит из операторов или составного оператора, то ничто не помешает в теле цикла располагать другие операторы цикла. Рассмотрим работу программы:
Program Prim21;
Var
i,j:integer;
BEGIN
for i:=1 to 5 do
Begin writeln;
for j:=20 to 23 do
write('i=',i,' j=',j);
end;
readln; END.
Для цикла for i:=1 to 5 do телом цикла является
begin for j:=20 to 23 do
write(' i= ', i, ' , j = ', j);
writeln;
end;
Назовем этот цикл внешним. Кроме того, имеется внутренний цикл
for j: = 20 to 23 do с телом write (' i = ', i , j =', j);
Работа программы. Вначале машина встречает внешний цикл и начинает его выполнять: присваивает i=l, затем переходит к его телу, а здесь встречает внутренний цикл и присваивает j значение 20, после чего выполняет тело внутреннего цикла, т.е. выводит на экран i=l, j=20. Так как внутренний цикл еще не окончен, то машина продолжает его выполнять, т.е. присваивает j значение 21 и добавляет к уже выведенной строке i=l, j=21.
Заметим, что оператор write отличается от оператора writeln тем, что он не начинает вывод с новой строки, а продолжает писать в той же строке, т.е. после второго выполнения внутреннего цикла на экране появится
i= 1, j=20 i= 1, j=21.
Машина продолжит выполнение внутреннего цикла, и, когда он закончится (выполнится для j = 20.21.22.23), на экране будет строка
i = 1 j = 20 i =l j = 21 i = 1 j = 22 i = 1 j = 23.
Внутренний цикл закончится, однако тело внешнего цикла еще не закончилось, поэтому выполняется оператор writeln, который переводит курсор на новую строку. После этого тело внешнего цикла закончится, но сам цикл отработал только для i = 1. Поэтому внешний цикл продолжит работу, присвоив i: =2 и вновь начав выполнение своего тела. Встретив внутренний цикл j:=1, на экран с новой строки выведется: i=2, j=20, затем j:=2 и к этой строке добавится i=2, j=21 и т.д., пока не закончится внутренний цикл.
Таким образом, внешний цикл, изменяя i от 1 до 5, заставит каждый раз выполняться полностью внутренний цикл, и в результате работы программы на экране появится:
i=l, j=20 i=1, j=21 i=1, j=22 i=1, j=23
i=2, j=20 i=2, j=21 i=2, j=22 i=2, j=23
i=3, j=20 i=3, j=21 i=3, j=22 i=3, j=23
i=4, j=20 i=4, j=21 i=4, j=22 i=4, j=23
i=5, j=20 i=5, j=21 i=5, j=22 i=5, j=23
Вкладывать циклы друг в друга можно любое количество раз, необходимо лишь помнить, что количество выполнений самого внутреннего тела цикла при этом будет расти в геометрической прогрессии. Так, например,
for i:=l to 100 do
for j:=l to 100 do
for k:=l to 100 do
writeln (i, j, k);
дает столбик цифр:
111
112
113
114