Язык программирования Паскаль

Определение данных в языке Паскаль. Особенности линейного вычислительного процесса. Характеристика разветвляющегося вычислительного процесса и условного оператора. Рассмотрение директивы компилятора и обработки ошибок ввода. Обзор открытых массивов.

Рубрика Программирование, компьютеры и кибернетика
Вид учебное пособие
Язык русский
Дата добавления 26.09.2017
Размер файла 635,7 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

I, num:integer;

r, Len, Dist, max : real;

begin

{найдем max при вводе данных}

max:=0; {т.к. расстояние не м.б. <0 }

num:=1; {на случай, если все точки (0,0)}

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

for i:=1 to 10 do begin

read (x[i], y[i]);

Dist:=sqrt (sqr(x[i]) + sqr (y[i]));

if dist > max then begin

max:=dist; {запомнили новое расстояние}

Num:=i; {и номер точки}

end;

end;

writeln ('Номер точки=',num,

' расстояние=',dist:8:2);

Len:=0;{длина ломаной - сумма длин сторон}

for i:=1 to 9 do begin

{у 10-й точки нет следующей!}

r:= sqrt (sqr(x[i]-x[i+1])+

sqr(y[i]-y[i+1]));

Len:=Len+r;

end;

writeln ('Длина ломаной=',len:8:2);

end.

Приведем пример задачи формирования массива по правилу.

Задан массив x из 8 элементов. Сформировать массив y по правилу

и найти количество его положительных элементов.

var x,y: array [1..8] of real;

i,k:integer;

begin

writeln ('Введите массив x из 8 эл.');

for i:=1 to 8 do begin

read (x[i]);

if i mod 2 =0 then y[i]:=4*x[i]

else y[i]:=cos(2*x[i]);

end;

K:=0;

writeln ('Массив y');

for i:=1 to 8 do begin

if y[i]>0 then k:=k+1;

write (y[i]:8:2);

end;

writeln;

writeln ('K=',k);

end.

15. Решение типовых задач на массивы

В гл. 14 мы подчеркивали, что типовые алгоритмы, изученные в теме "Циклы", в полной мере применимы и к массивам. Занимая определенный объем оперативной памяти, однажды полученные элементы массива остаются доступными весь сеанс работы программы и не требуют повторного вычисления или чтения из файла. Это порождает круг приложений, связанных с типовой обработкой наборов данных - вычисление математических и статистических характеристик векторов, объединение массивов или поиск нужных значений в них и т. д. Другие интересные примеры, такие как подсчет частоты встречаемости элементов в массиве, задача сортировки (упорядочения) данных, будут рассмотрены в гл. 16.

1. Найти сумму, скалярное произведение и длину двух векторов произвольно заданной размерности. Размерность не может превышать значения 100.

Предельную размерность массивов зададим константой size=100. Размерность реальных векторов a и b, с которыми работает программа, не обязательно столь велика, обозначим ее n и позаботимся о том, чтоб при вводе значения n соблюдалось соотношение 2?n?size. Введем вектор a с клавиатуры, а вектор b сгенерируем из случайных вещественных чисел, принадлежащих диапазону от 0 до 10. Для этого достаточно умножить на 10 значение, возвращаемое стандартной функцией random, если она вызвана без параметров: b[i]:=random*10;.

При написании кода мы применим изученный в гл. 8 прием контроля правильности вводимых данных с помощью директивы компилятора и стандартной функции IoResult.

Как известно из математики, сумма векторов a и b ищется поэлементно по правилу ci=ai+bi, скалярное произведение может быть вычислено по формуле , а длина вектора a из n элементов ищется по формуле .

const size=100;

var a,b,c:array [1..size] of real;

n,i:integer;

la,lb,s:real;

begin

repeat

writeln;

write ('Введите размерность A и B, ',

'значение от 2 до ',size,':');

{$I-}readln (n);{$I+}

if (IoResult<>0) or (n<2) or (n>size)

then

writeln ('Неверный ввод, повторите')

else break;

until false;

writeln ('Введите вектор A из ',

n,' элементов:');

for i:=1 to n do begin

repeat

write ('A[',i,']=');

{$I-}readln (a[i]);{$I+}

if IoResult=0 then break;

until false;

end;

writeln ('Генерируется вектор B из ',

n,' элементов:');

randomize;

for i:=1 to n do begin

b[i]:=random*10;

write (b[i]:8:2);

end;

la:=0;

lb:=0;

s:=0;

writeln;

writeln ('Вектор c=A+B:');

for i:=1 to n do begin

c[i]:=a[i]+b[i];

write (c[i]:8:2);

la:=la+sqr(a[i]);

lb:=lb+sqr(b[i]);

s:=s+a[i]*b[i];

end;

writeln;

writeln ('Длина вектора A: ',

sqrt(la):8:2);

writeln ('Длина вектора B: ',

sqrt(lb):8:2);

writeln ('Скалярное произведение:',s:8:2);

reset (input); readln;

end.

Думаю, при анализе листинга вы обратили внимание еще на одну особенность программы - на каждом шаге цикла ввода вектора a дополнительно выполняется цикл repeat...until, предназначенный для контроля правильности ввода элементов массива. Подробнее такие конструкции, называемые кратными циклами, будут изучены в гл. 16.

2. Задана выборка из n случайных чисел. Для n=100 определить математическое ожидание mx и дисперсию dx выборки по формулам: , .

Две подсчитываемые величины - самые распространенные статистические характеристики набора случайных данных. Фактически, математические ожидание характеризует арифметическое среднее данных выборки, а дисперсия - среднеквадратичное отклонение данных от среднего значения. Для хранения данных используем массив x из 100 элементов, сами данные сгенерируем из случайных чисел, находящихся в диапазоне от -1 до 1 включительно. Поскольку алгоритм накопления суммы предполагает последовательное и однократное использование каждого элемента массива, в цикле генерации элементов массива могут быть подсчитаны значения и , которые затем позволят найти mx и dx.

const n=100;

var x:array [1..100] of real;

s,d:real;

i:integer;

begin

writeln;

writeln ('Массив x[100]:');

randomize;

s:=0; d:=0;

for i:=1 to n do begin

x[i]:=random*2-1;

write (x[i]:8:3);

s:=s+x[i];

d:=d+sqr(x[i]);

end;

s:=s/n; {теперь в s - мат. ожидание,}

d:=d/(n-1)-sqr(s)/(n*(n-1));

{а в d - дисперсия}

writeln;

writeln ('s=',s:8:4);

writeln ('D=',d:8:4);

reset (input); readln;

end.

3. Объединить 2 упорядоченных по возрастанию массива A и B в массив C.

Для решения этой задачи тоже достаточно одного прохода по массивам. Действительно, заведя для каждого из массивов a, b и c по собственному счетчику (обозначим их ia, ib и i соответственно), мы можем, в зависимости от истинности или ложности соотношения aia?bib переписывать в элемент ci значение aia или bib. Остается проконтролировать, чтобы ни один из счетчиков не вышел за границу размерности своего массива. Увидеть детали вам поможет внимательный анализ листинга.

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

program m_concat;

const size=10;

step=5;

var a,b:array [1..size] of integer;

c:array [1..2*size] of integer;

i,n1,n2,ia,ib,ic:integer;

begin

writeln;

repeat

write('Размерность 1 массива ',

'(от 2 до ',size,'):');

read (n1);

until (n1>1) and (n1<=size);

randomize;

a[1]:=random(step);

write ('A= ',a[1],' ');

for i:=2 to n1 do begin

a[i]:=a[i-1]+random(step);

write (a[i],' ');

end;

writeln;

repeat

write('Размерность 2 массива ',

'(от 2 до ',size,'):');

read (n2);

until (n2>1) and (n2<=size);

b[1]:=random(step);

write ('B= ',b[1],' ');

for i:=2 to n2 do begin

b[i]:=b[i-1]+random(step);

write (b[i],' ');

end;

writeln;

ia:=1; ib:=1;

write ('c= ');

for i:=1 to n1+n2 do begin

if a[ia]<=b[ib] then begin

c[i]:=a[ia];

if ia<n1 then Inc(ia)

else begin

a[n1]:=b[ib];

if ib<n2 then Inc (ib);

end;

end

else begin

c[i]:=b[ib];

if ib<n2 then Inc(ib)

else begin

b[n2]:=a[ia];

if ia<n1 then Inc(ia);

end;

end;

write (c[i],' ');

end;

writeln;

end.

16. Кратные циклы

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

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

16.1 Двойной цикл и типовые задачи на двойной цикл

В теле цикла может находиться любой оператор, в том числе и другой оператор цикла. Такая конструкция называется кратными или вложенными циклами. Число вложений циклов не ограничено. Если цикл содержит только один вложенный цикл, то он называется двойным циклом.

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

Поясним сказанное на примере. В конструкции, описанной ниже:

for i:=1 to 5 do begin

{начало внешнего цикла}

j:=-5;

while j<=5+1e-6 do begin

{начало внутреннего цикла}

{тело двойного цикла}

j:=j+0.5;

end; {конец внутреннего цикла}

end; {конец внешнего цикла}

при i=1 переменная j последовательно принимает значения -5, -4.5, ... , 5, затем процесс повторится при i=2, 3, 4 и 5. Таким образом, за время выполнения каждого шага внешнего цикла for внутренний цикл while выполнится 21 раз.

Двойной цикл особенно широко распространен в программировании, так как идеально подходит для обработки табличной информации, работы с матрицами, задач сортировки и т. д. Рассмотрим несколько типовых задач, связанных с двойными циклами.

1. Для функции двух переменных f(x,y) = sin 2x + cos 4y построить таблицу ее значений при значениях аргументов x и y, меняющихся от 0 до 2 с шагом, равным 0.25.

Решение задачи возможно, как минимум, в трех вариантах. Они схематично изображены на рис. 16.1.

Варианты 1 и 2 отличаются лишь порядком следования внешнего и внутреннего циклов. Рассмотрим возможное решение для варианта 1.

Рис. 16.1. Таблицы значений функции 2 переменных

var x,y,f:real;

begin

writeln ('x':8,'y':8,'f':8);

x:=0;

while x<=2 do begin

y:=0;

while y<=2 do begin

f:=sin(2*x)+cos(4*y);

writeln (x:8:2,y:8:2,f:8:2);

y:=y+0.25;

end;

x:=x+0.25;

end;

end.

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

Вариант 3 удобен "естественным" представлением таблицы значений функции двух переменных, однако, помешать его реализации может ограниченная ширина окна консоли (80 символов). Ниже приводится одна из возможных реализаций такой программы:

var x,y,f:real;

begin

writeln; write (' ':6);

{оставляем место для печати значений y}

x:=0;

while x<=2 do begin

{ для печати строки значений x

потребовался отдельный цикл }

write (x:6:2);

x:=x+0.25;

end;

y:=0;

while y<=2 do begin

writeln;{перевод строки для очередного y}

write (y:6:2); {печать значения y}

x:=0; {реализация внутреннего цикла по x}

while x<=2 do begin

f:=sin(2*x)+cos(4*y);

write (f:6:2);

x:=x+0.25;

end;

y:=y+0.25;

end;

end.

Как видно из листинга, код несколько усложнился за счет дополнительного цикла формирования строки значений x, для того, чтобы строка таблицы помещалась на строке экрана, пришлось также уменьшить ширину поля вывода с 8 до 6 символов. Однако, выигрыш несомненен - теперь вся таблица поместилась на одном текстовом экране. Ограничение на ширину экрана консоли не имело бы значения, например, при выводе таблицы значений в текстовый файл.

2. Не обязательно границы внешнего и внутреннего циклов четко фиксированы. На практике распространены задачи, когда граница внутреннего цикла переменна и зависит от текущего значения управляющей переменной внешнего цикла. Рассмотрим такую задачу.

Напечатать на первой строке вывода один символ звездочки, на второй - два, на строке номер N - N символов. Значение N вводится с клавиатуры, 1<N<25.

В общем виде условие можно сформулировать так: в строке номер i печатается i звездочек, при этом i меняется от 1 до N включительно. Разумеется, для перебора значений i мы используем цикл for. Однако для печати очередной строки звездочек нам потребуется вложенный цикл. Обозначив его счетчик как j, получим закон изменения j от 1 до i включительно:

var i,j,n:integer;

begin

repeat {Ввод N с контролем ошибок}

write ('N=');

{$I-}read (n);{$I-}

if (IoResult<>0) or (n<2) or (n>24)

then begin

writeln ('Ошибка! N должно быть ',

'числом от 2 до 24');

continue;

end;

break;

until false;

{Двойной цикл, в котором вложенный

цикл имеет переменную границу}

for i:=1 to n do begin

writeln;

for j:=1 to i do

write ('*');

end;

end.

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

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

Итак, нам требуется получить порядок элементов в массиве, при котором выполняется соотношение A1?A2?...?AN. Достичь этого можно, сделав перестановки значений элементов, алгоритм перестановки нам известен еще из гл. 4. Представим, что в цикле мы сравниваем первый элемент A1 со всеми остальными Ai, i=2, 3, ... ,N, при необходимости (если A1>Ai) переставляя A1 и Ai. Таким образом, после выполнения этого цикла на первом месте в массиве гарантированно окажется его наименьший элемент. Для поиска следующего по величине элемента алгоритм можно повторить, сравнивая элемент A2 с остальными и при необходимости выполняя перестановку. Таким образом, приходим к двойному циклу следующего вида:

i=1,2,....,N-1 {до N-1 - так как у

элемента AN нет следующего}

j=i+1,i+2,...,N {будем для Ai перебирать

все следующие элементы}

{в теле двойного цикла сравниваем

Ai и Aj, при необходимости переставляем

их значения}

Реализуем этот алгоритм в виде следующей программы:

const nmax=100; {максимальная размерность}

var a:array [1..nmax] of real;

i,j,n:integer;

b:real; {буферная переменная}

begin

repeat {цикл ввода значения N с проверкой}

writeln ('Введите N от 2 до ',nmax,':');

{$I-}read (n);{$I+}

if (IoResult<>0) or (n<2) or (n>nmax)

then writeln ('Ошибка! Повторите ввод');

until (n>1) and (n<=nmax);

{Цикл ввода и печати исходного массива}

randomize;

{Элементы массива выберем случайными}

writeln ('Исходный массив:');

for i:=1 to n do begin

a[i]:= random(100);

write (a[i]:4:0);

end;

{Двойной цикл сортировки}

for i:=1 to n-1 do

for j:=i+1 to n do

if a[i]>a[j] then begin

b:=a[i]; a[i]:=a[j]; a[j]:=b;

end;

{Цикл вывода измененного массива}

writeln;

writeln ('Отсортированный массив:');

for i:=1 to n do write (a[i]:4:0);

reset (input); readln;

end.

Сортировка массива по убыванию отличается лишь знаком в операторе сравнения элементов Ai и Aj.

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

4. Найти, сколько раз каждый элемент встречается в массиве.

Первое, что приходит в голову при решении этой распространенной задачи - кроме массива данных ввести "массив частот", элементы которого будут хранить информацию о встречаемости тех или иных значений. Однако решить проблему можно и без столь расточительных трат оперативной памяти (ведь размерность массива частот будет равна размерности исходного массива). Каждый элемент массива ai, кроме последнего, мы будем сравнивать со следующими за ним элементами aj, считая сколько раз выполнилось равенство ai=aj. Для того чтобы исключить повторную обработку уже встречавшихся элементов, учтенные значения нам придется заменять нулями. Поскольку значение ноль само может встретиться в массиве, дополнительный цикл перед обработкой подсчитает количество нулевых элементов.

Константа size носит то же назначение, что nmax в предыдущей задаче, а константа Diap задает диапазон значений, которые могут принимать элементы массива. Элементы массива следует сделать целочисленными, ведь задача сравнения вещественных чисел в общем случае некорректна (см. п. 7.2).

program m_chast;

const size=10;

Diap=10;

var a:array [1..size] of integer;

i,n,k,j:integer;

begin

writeln;

repeat

write('Введите размерность 1 массива ',

'(от 2 до ',size,'):');

read (n);

until (n>1) and (n<=size);

randomize;

a[1]:=random(Diap);

write ('A= ',a[1],' ');

for i:=2 to n do begin

a[i]:=random(Diap);

write (a[i],' ');

end;

writeln;

k:=0;

for i:=1 to n do if a[i]=0 then Inc(k);

if k>0 then writeln ('0: ',k);

for i:=1 to n-1 do if a[i]<>0 then begin

k:=1;

for j:=i+1 to n do if a[i]=a[j] then

begin

a[j]:=0;

Inc(k);

end;

writeln (a[i],': ',k);

end;

end.

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

5. Координаты десяти точек на плоскости заданы двумя массивами xi, yi, i=1, 2, ..., 10. Найти две точки, расстояние между которыми минимально.

Действительно, нам придется искать расстояние от первой точки до всех остальных, от второй - только до точек с номерами 2, 3, ..., 10, так как расстояние до первой уже известно и т. д. Попробуйте написать программу самостоятельно, применив двойной цикл из предыдущего примера.

6. В завершение темы рассмотрим задачу, где число вложений циклов больше двух.

Номера автобусных билетов состоят из 6 цифр. "Счастливыми" называют билеты, в номерах которых сумма трех первых цифр равна сумме трех последних. Определить общее количество "счастливых" билетов.

Каждая из шести цифр может меняться от 0 до 9 включительно. Создав кратные циклы по шести переменным, воспользуемся известным нам алгоритмом организации счетчика:

var i,j,k,l,m,n:integer;

kol:longint;

{типа integer может быть недостаточно}

begin

kol:=0;

for i:=0 to 9 do

for j:=0 to 9 do

for k:=0 to 9 do

for l:=0 to 9 do

for m:=0 to 9 do

for n:=0 to 9 do

if i+j+k = l+m+n then inc(kol);

writeln

('Количество счастливых билетов=',kol);

end.

16.2 Оператор безусловного перехода

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

goto Метка;

Здесь Метка - специальным образом указанное место в программе. Действие goto очень просто: независимо от каких-либо условий он осуществляет передачу управления на оператор, следующий за меткой. Каждая метка имеет вид

m:

где m - имя, построенное по обычным правилам или целочисленная константа от 0 до 9999 включительно. Метку недостаточно поставить перед нужным оператором - ее нужно еще и объявить в разделе описаний оператором

label m;.

Можно объявить и несколько меток сразу, разделив их имена запятыми. Принято располагать оператор описания меток до оператора var. Таким образом, метки и оператор goto позволяют решить задачу быстрого непосредственного перехода к нужному месту в программе:

label 10,20;

var i,j:integer;

begin

write ('Введите значение I (i>2)');

readln (i);

if i<3 then goto 10;

j:=2;

20:

if i mod j = 0 then write (j,' ');

j:=j+1;

if j<i then goto 20;

10:

end.

Эта несложная программа позволяет пользователю ввести число i, большее двух, после чего печатает все числа от 2 до i-1, на которые i делится без остатка (проверка остатка от деления i на j выполнена операцией mod). В случае ввода пользователем недопустимого значения i (меньшего трех) происходит переход на метку 10:, расположенную перед закрывающим оператором end. - таким образом, программа сразу же завершается. Метка 20: позволяет организовать цикл без использования операторов while, repeat или for. Действительно, перед меткой 20: переменная j получает начальное значение 2, затем, если i делится на j без остатка, значение j выводится на экран, затем увеличивается на единицу и, если значение i не достигнуто, совершается переход на метку 20:, проверяющую уже новое значение j. Однако, и той, и другой цели можно было достигнуть без использования goto, применяя только операторы цикла и break;.

С правомерностью использования простого оператора goto связаны сложные и многолетние споры. Несомненно, что программу всегда можно написать, не прибегая к goto (иногда структурное программирование называют даже "программированием без goto"), не вызывает сомнения также, что использование меток и goto нарушает и усложняет восприятие структуры программы человеком. Однако автору представляется чрезмерным требование изгнать goto совсем - в ряде ситуаций он просто удобнее, чем другие средства. Представим, что нам нужно досрочно выйти из двойного (или вообще кратного) цикла. Оператор break;, расположенный во внутреннем цикле, прервет его выполнение, но выполнение внешнего цикла продолжится со следующего шага. Без goto выйти "из всех циклов сразу" можно разве что с применением специальных переменных-флагов:

var Flag:boolean; {переменная-флаг}

...

Flag:=false; {флаг установлен в "ложь"}

while true do begin {внешний цикл}

while true do begin {внутренний цикл}

...

if Условие then begin

{случилось что-то, требующее выхода

из обоих циклов сразу}

Flag:=true;

{ставим флаг, сообщая внешнему циклу,

что требуется завершение}

break; {выход из внутреннего цикла}

end;

end;

if Flag=true then break;

{внешний цикл на каждом шаге должен

отслеживать состояние флага}

end;

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

label go1;

...

while true do begin

while true do begin

...

if Условие then goto go1;

{вышли из двух циклов сразу!}

end;

end;

Go1:

...

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

Ограничения на использование goto следующие: метка должна находиться в том же блоке программы, что и оператор goto. Таким образом, нельзя перейти из одной части сложной программы (такой, как процедура или функция, изучаемые в гл. 18) в другую. Несомненно, что эти ограничения введены разработчиками, чтоб не усложнять и без того сложное восприятие программ, злоупотребляющих данным оператором.

17. Матрицы и типовые алгоритмы обработки матриц

Многие задачи связаны с обработкой многомерных массивов данных. Наиболее распространены при этом двумерные массивы.

В математике двумерные массивы представляются матрицей:

или, в сокращенной записи,

,

где n - число строк матрицы, m - число столбцов, i, j - индексы (номера) текущих строки и столбца, на пересечении которых находится элемент .

Как и другие объекты данных, матрица описывается в разделе var. От вектора ее описание отличается тем, что в квадратных скобках перечисляются два оператора диапазона, указывающие, соответственно, нумерацию строк и столбцов матрицы:

var ИмяМатрицы: array

[n1..n2, m1..m2] of Тип;

Здесь n1..n2 - диапазон значений номера строки, n1 и n2 - целочисленные константы, m1..m2 - диапазон значений номера столбца, значения m1 и m2 также целочисленные.

Как и векторы, матрицы могут быть образованы из элементов любого существующего в языке типа данных.

Под матрицу из n строк и m столбцов выделяется область памяти размером n*m*k байт, где k - размер в байтах одного элемента. Для известных нам типов данных этот размер можно узнать из табл. 2.1. В оперативной памяти матрица хранится всегда построчно.

Например, для матрицы A, описанной оператором вида

var A:array [1..5,1..4] of real;

выделяется 20 ячеек памяти по 6 байт, причем в следующей за элементом A1,4 ячейке хранится значение элемента A2,1.

Обращение к отдельным элементам матрицы осуществляется с помощью переменной с двумя индексами, например:

ai,j a[i,j]

a2,1 a[2,1]

a2n,k a[2*n,k]

Первый индекс, как и в математике, всегда показывает номер строки, а второй - номер столбца.

Поскольку адресация памяти в любом случае линейна, следует понимать матрицу как удобный для программиста структурный тип данных. В отдельных случаях использование матрицы может быть заменено использованием вектора с тем же количеством элементов: так, матрице An,m всегда может быть сопоставлен вектор b из n*m элементов, а обращение к элементу A[i,j] при нумерации строк и столбцов с единицы может быть заменено на обращение к элементу b[(i-1)*m+j].

Подобно тому, как любая последовательная обработка вектора выполняется в цикле for, обработка матрицы осуществляется в двойном цикле for:

for i:=1 to n do

for j:=1 to m do

{Обработка элемента A[i,j]}

Согласно правилу выполнения кратных циклов, переменная j меняется в этом двойном цикле быстрее, чем i, таким образом, обработка всех элементов матрицы будет выполнена построчно. Для последовательной обработки всех элементов матрицы по столбцам достаточно поменять местами циклы по i и j:

for j:=1 to m do

for i:=1 to n do

{Обработка элемента A[i,j]}

Теоретически мы могли бы решить эту же задачу и перестановкой индексов в обращении к элементу матрицы (использовать запись A[j,i] вместо A[i,j]), однако, во избежание путаницы, делать этого не рекомендуется.

Приведем примеры использования двойного цикла for для ввода и вывода элементов матрицы. Пусть матрица c размерностью 42 (как мы помним, это означает, что в матрице 4 строки и 2 столбца) описана оператором вида

var c:array [1..4,1..2] of real;

В программе предусмотрено также 2 целочисленных счетчика для строк и столбцов матрицы:

var i,j:integer;

В этом случае типовой ввод матрицы с клавиатуры мог бы выглядеть так:

writeln ('Введите матрицу c ',

'размерностью 4*2');

for i:=1 to 4 do

for j:=1 to 2 do read (c[i,j]);

Иногда удобнее печатать отдельное приглашение к вводу каждого элемента:

writeln ('Введите матрицу c[4,2]');

for i:=1 to 4 do

for j:=1 to 2 do begin

write ('c[',i,',',j,']=');

readln (c[i,j]);

end;

Например, в качестве приглашения к вводу элемента c1,1 на экране будет напечатано:

c[1,1]=

Оператор readln используется, поскольку элементы вводятся по одному.

Как и для векторов, возможны альтернативные способы ввода - описание матрицы в разделе констант и формирование ее элементов из случайных чисел. Приведем пример описания матрицы констант размерностью 23:

const d:array [1..2,1..3] of integer=(

(1,2,3),

(4,5,6)

);

Как видно из примера, для описания матрицы констант создается список ее строк, каждая строка, в свою очередь, представляет собой список значений элементов.

Вывод на экран матрицы небольшой размерности бывает удобно реализовать в виде "одна строка матрицы на одной строке экрана". Для этого элементы матрицы во внутреннем цикле печатаются оператором write, не переводящим строку на экране, а во внешнем цикле выполняется writeln:

writeln ('Матрица c:');

for i:=1 to 4 do begin

writeln;

for j:=1 to 2 do write (c[i,j]:4:1);

end;

Разумеется, при выводе элементов матрицы можно использовать константы ширины и точности.

Базовые алгоритмы обработки матриц - те же, что мы изучили в гл. 11-15. Отличие состоит в том, что реализацию этих алгоритмов можно условно рассматривать для двух типов задач:

алгоритмы реализуются при обработке всех элементов матрицы;

алгоритмы реализуются при обработке отдельных строк или столбцов матрицы.

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

1. Приведем пример реализации алгоритма первого типа.

В матрице A размерностью 4*4 найти сумму ее положительных элементов, произведение элементов, значения которых попадают в интервал [2, 10], а также отношение этих двух величин.

var a:array [1..4,1..4] of real;

i,j:integer;

s,p,z:real;

begin

{Цикл ввода}

writeln ('Ввод матрицы размерностью 4*4');

for i:=1 to 4 do

for j:=1 to 4 do read (a[i,j]);

{Начальные значения и цикл обработки}

s:=0;

p:=1;

for i:=1 to 4 do

for j:=1 to 4 do begin

if a[i,j]>0 then s:=s+a[i,j];

if (a[i,j]>=2) and (a[i,j]<=10)

then p:=p*a[i,j];

end;

{Вывод результатов}

writeln ('Сумма=',s:6:2);

writeln ('Произведение=',p:6:2);

if p<>0 then begin

z:=s/p;

writeln ('Отношение=',z:6:2);

end

else writeln ('p=0, отношение ',

'вычислить нельзя');

reset (input); readln;

end.

Поскольку о типе матрицы в условии задачи ничего не сказано, выбран "более общий" тип real. Способ ввода также выбран "по умолчанию" - пользователь вводит значения элементов матрицы с клавиатуры. Искомые сумма, произведение и отношение обозначены, соответственно, s, p и z.

2. Рассмотрим пример реализации алгоритма второго типа. Отличие его в том, что алгоритм подсчета или поиска каждый раз заново реализуется во внутреннем цикле, таким образом, искомая в двойном цикле величина заново получает значение на каждом шаге внешнего цикла.

Задана матрица C размерностью 34. Найти номер строки, сумма элементов которой максимальна.

На этот раз заполним матрицу случайными числами. Обозначим s сумму элементов очередной строки, max - максимальную из этих сумм. В качестве искомого номера строки введем целочисленную переменную imax, в которую будем заносить номер текущей строки i, если для этой строки выполняется соотношение s>max. Переменную s следует инициализировать на каждом шаге внешнего цикла по i (ведь сумма элементов ищется заново в каждой строке), тогда как переменная max ищется для всей матрицы и инициализируется один раз до двойного цикла:

var c:array [1..3,1..4] of real;

i,j,imax:integer;

s,max:real;

begin

{Формирование матрицы с ее выводом}

writeln;

write ('Матрица C');

for i:=1 to 3 do begin

writeln;

for j:=1 to 4 do begin

c[i,j]:=random*10

{вещественные числа от 0 до 10};

write (c[i,j]:6:2);

end;

end;

{Поиск номера строки и вывод результата}

max:=-1e30;

for i:=1 to 3 do begin

s:=0;

for j:=1 to 4 do s:=s+c[i,j];

if s>max then begin

max:=s;

imax:=i;

end;

end;

writeln;

writeln ('Номер строки=',imax);

end.

Аналогично могут быть реализованы типовые алгоритмы в столбце матрицы - как описано выше, для этого достаточно поменять местами циклы по i и j.

3. Встречаются также задачи, где результаты обработки строк или столбцов матрицы должны быть занесены в вектор. Решим задачу подобного типа.

В матрице S размерностью 204 приведены оценки 20 студентов за 4 экзамена последней сессии. Подчитать и вывести на экран элементы вектора, содержащего средние баллы каждого студента.

Обозначим искомый вектор как B. Его размерность очевидна, поскольку каждый элемент вектора зависит от одной строки матрицы. Применив алгоритм обработки строк матрицы, получаем следующую программу:

var s:array [1..20,1..4] of integer;

b:array [1..20] of real;

i,j:integer;

begin

{Матрица s из случайных оценок от 3 до 5}

for i:=1 to 20 do

for j:=1 to 4 do s[i,j]:=3+random(3);

{Обрабатываем матрицу построчно,

формируя вектор B}

for i:=1 to 20 do begin

b[i]:=0;

for j:=1 to 4 do b[i]:=b[i]+s[i,j];

b[i]:=b[i]/4;

end;

{Выводим оценки со средними баллами}

writeln;

write ('Оценки':8,' Баллы');

for i:=1 to 20 do begin

writeln;

for j:=1 to 4 do write (s[i,j]:2);

write (b[i]:5:2);

end;

end.

4. Нередко встречаются задачи, требующие обработки элементов, расположенных под или над главной диагональю матрицы. Главная диагональ характеризуется тем, что номера строк и столбцов расположенных на ней элементов совпадают. Корректно это понятие определено только для квадратных матриц с одинаковым числом строк и столбцов. Соответственно, для обработки элементов, лежащих на главной диагонали матрицы A размерностью nn было бы достаточно цикла

for i:=1 to n do begin

{Обработка элемента A[i,i]}

end;

Когда требуется обработать элементы, лежащие под главной диагональю (для них номер строки больше номера столбца), двойной цикл обработки мог бы выглядеть так:

for i:=1 to n do

for j:=1 to n do begin

if i>j then begin

{Обработка элемента A[i,j]}

end;

end;

Приведенный код имеет тот недостаток, что в нем выполняются заведомо лишние шаги цикла. Действительно, для квадратной матрицы размерностью nn мы выполняем n2 шагов двойного цикла, тогда как имеется всего элементов под главной диагональю. Исправить ситуацию может двойной цикл с переменной границей, подобный изученному выше:

for i:=1 to n do

for j:=1 to i-1 do begin

{Обработка элемента A[i,j]}

end;

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

for i:=1 to n do

for j:=i+1 to n do begin

{Обработка элемента A[i,j]}

end;

5. Распространенный класс задач, связанных с умножением и обращением матриц, мы рассмотрим в гл. 18. Сейчас же ограничимся реализацией хорошо известного алгоритма умножения матрицы A размерностью nm на вектор b размерности m. Напомним, что в результате умножения получается вектор размерности n (обозначим его c), а само умножение состоит в том, что каждая строка матрицы скалярно умножается на вектор по формуле и результат заносится в компоненту ci. Применив известные нам типовые алгоритмы, напишем следующую программу:

const n=3; m=4;

{размерности матрицы и векторов}

var a:array [1..n,1..m] of real;

b:array [1..m] of real;

c:array [1..n] of real;

i,j:integer;

begin

{Формируем матрицу A из случайных чисел}

writeln ('Матрица A');

for i:=1 to n do begin

for j:=1 to m do begin

a[i,j]:=random*10;

write (a[i,j]:6:2);

end;

writeln;

end;

{Аналогично для вектора b}

writeln ('Вектор b');

for j:=1 to m do begin

b[j]:=random*10;

writeln (b[j]:6:2);

end;

{Умножение совмещаем с выводом вектора с,

это возможно, т.к. компоненты вектора

ищутся последовательно}

writeln ('Вектор c=A*b');

for i:=1 to n do begin

c[i]:=0; {c[i] - сумма}

for j:=1 to m do

c[i]:=c[i]+A[i,j]*b[j];

writeln (c[i]:6:2);

end;

end.

18. Подпрограммы

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

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

Использование подпрограмм в чем-то похоже на расчеты с использованием математических или физических формул. Так, имея общие формулы решения квадратного уравнения, мы можем подставить вместо коэффициентов a, b и c любые числовые значения и решить любое конкретное уравнение. Аналогично, мы могли бы написать подпрограмму с входными параметрами a, b, c и выходными параметрами x1, x2 (найденные корни уравнения), а затем, применяя нужное число раз эту подпрограмму (однократное использование подпрограммы называется ее вызовом), найти корни любого количества квадратных уравнений.

Итак, использование подпрограмм позволяет решить следующие задачи:

уменьшение размеров кода и экономия памяти за счет возможности неоднократного вызова одной и той же подпрограммы в рамках одной программы;

лучшее структурирование программы за счет разбиения задачи на более простые подзадачи;

эффективное повторное использование однажды написанного кода.

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

var раздел_описаний_1;

Заголовок Подпрограммы_1;

begin

{Тело подпрограммы_1}

end;

Заголовок Подпрограммы_2;

begin

{Тело подпрограммы_2}

end;

var раздел_описаний_2;

begin

{Тело главной программы}

end.

Как видно из листинга, каждая подпрограмма имеет заголовок (по меньшей мере, в этом заголовке должно быть указано ее назначенное программистом имя) и тело, состоящее из операторов. Подобно телу цикла, тело подпрограммы заключено в операторные скобки begin ... end;. Обратите внимание, что в листинге два раздела описаний. Первый из них расположен до обеих подпрограмм, второй - после них перед телом главной программы. Данные, описанные в первом разделе - глобальные, они доступны всем частям программы, расположенным ниже по ее тексту. Данные второго раздела описаний также глобальны, но доступны лишь главной программе, так как описаны непосредственно перед ней. Общее правило очень простое: подпрограммы "видят" все глобальные переменные, описанные выше их тела. Аналогично, без принятия специальных мер подпрограмма "видит" и может вызвать любую другую подпрограмму, расположенную выше нее по тексту программы. Здесь вторая подпрограмма может вызвать первую, но не наоборот. Главная программа, как правило, расположенная последней, может вызвать все подпрограммы.

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

var i:integer;

{глобальная переменная -

описана вне всех подпрограмм}

Заголовок Подпрограммы;

var i:integer;

{локальная переменная -

описана после заголовка подпрограммы}

begin

{Тело подпрограммы}

end;

begin

{Тело главной программы}

end.

Описанная перед телом подпрограммы локальная переменная i не имеет никакого отношения к одноименной переменной, описанной выше. На время выполнения подпрограммы локальная переменная i вытесняет глобальную, делая значение последней недоступной. После завершения подпрограммы значение локальной i будет утеряно, а значение глобальной i никак от этого не изменится. Итак, глобальные переменные видимы (доступны) от точки их определения до конца файла. Локальные переменные доступны только в том блоке операторных скобок, в котором они описаны и во вложенных в него блоках.

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

18.1 Процедуры

Общий вид подпрограммы-процедуры следующий:

procedure Имя

(Список формальных параметров);

{выше - заголовок подпрограммы}

var описания локальных переменных;

begin

{Тело процедуры}

end;

Другие подразделы раздела описаний, такие как label, const и изучаемый в п. 18.3 оператор type, также могут присутствовать между заголовком и телом процедуры и действие их также будет локально - то есть, определено лишь в рамках данной процедуры. Единственная правильная связь процедуры с "внешним миром", то есть, другими подпрограммами и главной программой - указанный после имени список формальных параметров. В этом списке через запятую указываются входные и выходные параметры процедуры с указанием их типов данных. Входные параметры служат исходными данными для процедуры, а выходные определяют результаты ее вычислений, передаваемые в главную программу или другую подпрограмму. Перед выходными параметрами, измененные значения которых должны сохраняться после завершения процедуры, следует указывать ключевое слово var. Причины именно такого указания мы обсудим ниже.

Само по себе написание процедуры еще не вызывает выполнения никаких действий. Чтобы процедура сработала, ее нужно вызвать, записав в нужной точке программы имя процедуры со списком фактических параметров, которые будут подставлены на место формальных. Все это делается не так сложно, как звучит. Представим, что мы решили написать процедуру решения любого квадратного уравнения с именем Equation. Входными данными этой процедуры будут значения коэффициентов уравнения a, b и c, выходными - найденные корни x1 и x2 (если они существуют). Кроме того, нам понадобится "временная" (а точней, локальная) переменная d, позволяющая процедуре хранить вычисленное значение дискриминанта:

procedure Equation (a,b,c:real;

var x1,x2:real);

var d:real;

begin

d:=sqr(b)-4*a*c;

if d>=0 then begin

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

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

end;

end;

Поскольку все параметры этой процедуры - вещественные, в ее заголовке нам хватило двух списков - один для входных параметров a, b и c, другой - для выходных x1 и x2 (обратите внимание на слово var перед этим списком). В случае отрицательного значения дискриминанта, значения x1 и x2 остаются неопределенными (что, вообще говоря, не совсем корректно), в противном случае они вычисляются и должны быть переданы в главную программу. Вызвать эту процедуру мы могли бы, например, так:

var a,b,c,d,x1,x2,x3,x4:real;

...

write ('Введите значения a,b,c:');

read (a,b,c);

Equation (a,b,c,x1,x2);

Здесь мы попытались решить уравнение ax2+bx+c=0, значения a, b, c, ввел пользователь, ответы, если они вычислялись, будут помещены в переменные x1 и x2. При вызове вида

Equation (1,4,-3.5,x3,x4);

мы решили уравнение x2+4x-3.5=0, а ответы поместили в переменные x3 и x4. Еще один вызов

Equation (4,1,-3.5,x3,x4);

позволяет решить уравнение 4x2+x-3.5=0, записав ответы в переменные x1 и x2. Наконец, четвертое обращение к процедуре

Equation (1,b,0,x1,x3);

решает уравнение x2+bx=0, помещая ответы в переменные x1 и x3.

И так далее. Суть в том, что при каждом вызове подпрограммы значения фактических параметров подставляются на место формальных и с ними производятся вычисления, предусмотренные операторами подпрограммы. Указанные требования называют согласованием параметров и описывают следующим образом: формальные и фактические параметры должны быть согласованы между собой по количеству, типу и порядку следования. Это означает, что количество формальных и фактических параметров должно быть одинаковым, при этом, при вызове процедуры каждый фактический параметр должен иметь тот же тип и занимать в списке то же место, что и соответствующий ему формальный параметр. Из сказанного следует, что нашу процедуру Equation можно вызвать только с пятью параметрами (а не тремя, семью или нулём), причем все эти параметры должны быть вещественными. Если формальный параметр является выходным (перед ним в заголовке процедуры указано ключевое слово var), то соответствующий фактический параметр не может быть константой (ведь значение константы нельзя изменить). Для больше наглядности опишем данное соответствие в табл. 18.1.

Таблица 18.1 - Соответствие формальных и фактических параметров

Формальный параметр в заголовке процедуры

Соответствующий фактический параметр при вызове процедуры

Переменная некоторого типа данных без ключевого слова var

Переменная, константа, элемент массива или значение выражения того же типа данных

Переменная некоторого типа данных с ключевым словом var

Переменная или элемент массива того же типа данных

Массив

Массив

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

procedure p1 (x:integer);

{для x создастся локальная копия}

begin

x:=x+1; {значение копии увеличилось на 1}

writeln ('x=',x); {и было выведено}

end;

var x:integer;

begin

x:=3;

p1(x);

writeln ('x=',x);

{после вызова с передачей параметра

по значению, x по-прежнему равно 3}

end.

Если формальный параметр процедуры помечен ключевым словом var как параметр-переменная, это означает, что передача фактического параметра осуществляется по адресу, то есть, в процедуру передается адрес того места в памяти, где находится фактический параметр. Таким образом, все выполняемые в процедуре изменения значения параметра-переменной будут выполнены непосредственно над значением фактического параметра. По той же причине формальному параметру-переменной может соответствовать только фактический параметр-переменная - ведь значение константы нельзя изменить, а выражение не имеет конкретного адреса в памяти, вычисляясь каждый раз при выполнении программы:

procedure p1 (var x:integer);

{получаем адрес переменной,

переданной в качестве x}

begin

x:=x+1; {значение x увеличилось на 1}

writeln ('x=',x); {и было выведено}

end;

var x:integer;

begin

x:=3;

p1(x);

writeln ('x=',x);

{после вызова с передачей параметра

по ссылке,x стало равно 4 -

оно было изменено процедурой p1}

end.

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

Перейдем к примерам.

1. Для точки на плоскости с заданными координатами (x,y) найти расстояние l от точки до начала координат, а также длину окружности и площадь круга радиусом l с центром в начале координат.

Обозначим длину окружности как c, а площадь круга - s. Нетрудно заметить, что помимо выделения в отдельную процедуру расчета величин l, c и s, целесообразно написать также процедуру Get для ввода вещественного значения с предварительным выводом приглашения к вводу (нам придется вводить, по меньшей мере, 2 значения - x и y), а также процедуру Put для печати вещественного значения с указанной шириной, точностью и строкой комментария (выводиться будут искомые величины l, c и s).

procedure Circle2 (x,y:real;

var l,c,s:real);

const pi=3.1415926;

{для иллюстрации; на самом деле

есть функция pi}

begin

l:=sqrt(sqr(x)+sqr(y));

c:=2*pi*l;

s:=pi*sqr(l);

end;

procedure Get (s:string; var x:real);

{s - приглашение,

x - параметр-переменная,

вводимая с клавиатуры величина}

begin

write (s,': ');

readln (x);

end;

procedure Put (s:string; x:real);

{s - комментарий к выводу,

x - выводимая величина}

begin

writeln (s,'= ',x:8:3);

end;

var x,y,c,s,l:real;

begin

writeln;

Get ('Введите координату x',x);

Get ('Введите координату y',y);

Circle2 (x,y,l,c,s);

Put ('Удаление от начала координат',l);

Put ('Длина окружности',c);

Put ('Площадь круга',s);

end.

Несмотря на то, что процедура Circle2 вызвана в этой программе однократно, она может пригодиться при повторном использовании кода. Исходными данными (параметрами-значениями) для этой процедуры являются координаты точки x и y, выходными данными (параметры-переменные) - найденные значения l, c и s.

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

2. Написать процедуру, печатающую первые N членов ряда Фибоначчи.

Члены ряда Фибоначчи вычисляются по формуле:

F0 = 0, F1 = 1, FN = FN-1 + FN-2, N=2,3,...

Заметим, что кроме простой и красивой формулы, ряд Фибоначчи замечателен еще тем, что отношение двух его соседних членов дает в пределе константу "золотого сечения", широко используемую в самых различных расчетах. Для вычисления и вывода на экран N первых членов ряда Фибоначчи напишем процедуру Fibo с формальным параметром all, определяющим, сколько членов ряда требуется найти. В главной программе организуем цикл ввода значения фактического параметра N.

procedure Fibo (all:integer);

var n,n1,n2:longint;

{n - текущий элемент ряда,

n1 - предыдущий,

n2 - вычисленный 2 шага назад}

i:integer;

begin

n1:=1; n2:=0;

writeln;

write (n2,' ',n1,' ');

for i:=2 to all do begin

n:=n1+n2;

write (n,' ');

n2:=n1; n1:=n;

{переприсвоили для следующего шага}

end;

end;

var n:integer;

begin

repeat

writeln ('Введите число членов ряда ',

'или 0 для выхода:');

read (n);

if n=0 then halt(1)

else Fibo(n);

until false;

end.

18.2 Функции

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

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

function Имя

(Список формальных параметров) :

ТипРезультата;

{выше - заголовок подпрограммы}

var описания локальных переменных;

begin

{Тело функции}

Имя:=Выражение1;

end;

Здесь Выражение1 должно иметь тот же тип, что и указанный в заголовке ТипРезультата, а оператор Имя:=Выражение1; не обязан быть последним в теле функции. Поскольку результат выполнения функции возвращается в основную программу через ее имя, то обращение к функции записывается аналогично стандартным функциям в виде операнда-выражения, стоящего справа от знака присваивания:

Результат:=Выражение2;

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

Все, сказанное выше о согласовании формальных и фактических параметров, а также о параметрах-значениях и переменных, в полной мере относится и к функциям.

Фактически, стандартные подпрограммы Паскаля также делятся на функции и процедуры, в зависимости от способа их вызова. Так, writeln относится к стандартным процедурам, а sin - к функциям. Правда, некоторые из стандартных подпрограмм имеют переменное число параметров, что запрещено пользовательским подпрограммам.

Формально при использовании функций не запрещено возвращать более одного значения через дополнительные параметры-переменные, подобные тем, что мы изучили для процедур. Можно сказать, что функции в Паскале - это процедуры, способные возвращать дополнительное скалярное значение. Во многих других языках программирования специального разделения на процедуры и функции нет.

Чтобы окончательно уяснить не-синтаксические различия между функциями и процедурами, обобщим их в табл. 18.2.

Таблица 18.2 - Различия между процедурами и функциями

Процедура

Функция

Вызывается отдельным оператором

Вызывается из выражения справа от знака присваивания

Использует параметры-значения и переменные

Использует параметры-значения и переменные, дополнительно доступен параметр-переменная с именем, совпадающим с именем функции

Напишем и вызовем простейшую функцию, находящую максимальный из двух своих вещественных аргументов:

function max(a,b:real):real;

begin

if a>b then max:=a

else max:=b;

end;

Вызвать эту функцию мы могли бы различными способами:

...

Подобные документы

  • Язык программирования Турбо Паскаль. Запись алгоритма на языке программирования и отладка программы. Правила записи арифметических выражений. Стандартное расширение имени файла, созданного системным редактором. Составной оператор и вложенные условия.

    курсовая работа [75,0 K], добавлен 21.03.2013

  • Особенности программирования на языке Паскаль в среде Турбо Паскаль. Линейные алгоритмы, процедуры и функции. Структура данных: массивы, строки, записи. Модульное программирование, прямая и косвенная рекурсия. Бинарный поиск, организация списков.

    отчет по практике [913,8 K], добавлен 21.07.2012

  • Иерархическая структура производного типа данных в языке Паскаль. Определение массива как упорядоченного набора фиксированного количества некоторых значений. Сортировка одномерных и двумерных массивов методом простых обменов, простым выбором и включением.

    курсовая работа [48,8 K], добавлен 27.11.2010

  • История и основы структурного программирования в среде Turbo Pascal. Работа с различными типами данных. Операторы языка. Работа с символьными и строковыми переменами, одномерным, двумерным массивами. Классификация компьютерных игр. Игры на языке Паскаль.

    курсовая работа [28,8 K], добавлен 06.05.2014

  • Логические конструкции в системе программирования Паскаль. Команды языка программирования, использование функций, процедур. Постановка и решение задач механики в среде системы Паскаль. Задачи статики, кинематики, динамики решаемые с помощью языка Паскаль.

    курсовая работа [290,9 K], добавлен 05.12.2008

  • Общая характеристика языков программирования. Описание языка Паскаль: основные субъекты языка; структура Паскаль-программы; типизация и объявление данных. Операторы присваивания и выражения. Структурные операторы, организация ветвлений и циклов.

    дипломная работа [276,6 K], добавлен 26.01.2011

  • Сравнительный анализ языков программирования высокого уровня Си и Паскаль. Реализация алгоритма обработки данных. Тестирование и отладка программы или пакета программ. Структура программы на языке Турбо Паскаль. Указатели и векторные типы данных.

    курсовая работа [233,5 K], добавлен 14.12.2012

  • Изучение символьных и строковых типов данных, алгоритма задачи на языке программирования Паскаль. Описания получения и установки отдельного символа строки, изменения регистра символов. Анализ создания и просмотра файла, поиска и сортировки информации.

    курсовая работа [440,7 K], добавлен 13.06.2011

  • Совместимость и преобразование типов данных. Создание информационно-поисковой системы на языке программирования Паскаль. Описание интерфейса, каждого блока программы "Картотека больных". Рассмотрение результатов работы программы, сортирования данных.

    курсовая работа [368,9 K], добавлен 18.05.2015

  • Изучение основных конструкций и способов написания программ на языке Паскаль. Обзор принципов работы и интерфейса написанной программы. Обработка и модификация двумерных массивов. Файловые структуры данных. Текстовые файлы. Элементы машинной графики.

    курсовая работа [761,7 K], добавлен 11.03.2015

  • Приобретение теоретических и практических навыков программирования на языке Паскаль. Математическая формулировка задачи и выбор метода обработки информации. Разработка алгоритма и его описание. Описание программы. Форма представления исходных данных.

    курсовая работа [224,3 K], добавлен 11.02.2016

  • Основные сведения о системе программирования Турбо Паскаль. Структура программы на Паскале и ее компоненты. Особенности и элементы языка Турбо Паскаль. Порядок выполнения операций в арифметическом выражении, стандартные функции и оператор присваивания.

    лекция [55,7 K], добавлен 21.05.2009

  • Рассмотрение общих сведений и уровней языков программирования. Ознакомление с историей развития, использования языков программирования. Обзор достоинств и недостатков таких языков как Ассемблер, Паскаль, Си, Си++, Фортран, Кобол, Бейсик, SQL, HTML, Java.

    курсовая работа [759,5 K], добавлен 04.11.2014

  • Выбор метода проектирования транслятора с языка Паскаль на язык Си, разработка и кодирование алгоритма программы. Использование допустимых операторов в исходном тексте, определение типов переменных и синтаксиса логических и арифметических выражений.

    курсовая работа [1,0 M], добавлен 03.07.2011

  • Основы создания фрактальных композиций при помощи среды программирования Паскаль. Сущность и основа фрактальной графики. Графические возможности и операторы для рисования геометрических фигур в среде Паскаль. Примеры обозначения цветов на языке Паскаль.

    лабораторная работа [15,7 K], добавлен 12.06.2010

  • Понятие программы и ее основные составляющие. Операторы ввода и вывода. Разветвляющиеся алгоритмы. Цикл как многократное выполнение одинаковых действий. Особенности использования оператора выбора. Графики функций и системы координат. Рекурсия и анимация.

    презентация [663,7 K], добавлен 24.12.2010

  • Изучение организации диалоговой программы и закрепления основных элементов программирования на языке Паскаль и Си (Delphi, C++ Builder). Описание представления информации в программах на языках высокого уровня. Сравнительная характеристика Delphi и C++.

    курсовая работа [3,1 M], добавлен 27.02.2015

  • Алгоритмы и алфавит языка Турбо Паскаль. Основные типы данных. Операторы присваивания, перехода и выбора. Понятие массива в Паскале. Особенности работы со строками в программе. Использование линейного поиска и поиска с барьером. Основные виды сортировок.

    учебное пособие [53,2 K], добавлен 09.11.2009

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

    курсовая работа [254,0 K], добавлен 02.07.2011

  • Расчет на устойчивость в системе программирования Турбо Паскаль. Определение критического напряжения стержня по формуле Ясинского, предельной гибкости в зависимости от материала. Программирование алгоритма расчета. Порядок ввода исходных данных.

    курсовая работа [1,0 M], добавлен 27.04.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.