Массивы в качестве параметров подпрограммы
Зачастую подпрограмма-процедура или функция должна обработать некоторый массив данных. Однако структурный тип массива нельзя непосредственно указать в списке формальных параметров. В этом случае тип массива объявляется заранее в разделе описаний с помощью подраздела type: type ИмяТипа = ОписаниеМассива; Здесь ИмяТипа -- даваемое программистом наименование нового типа данных, а ОписаниеМассива -- известный нам оператор описания вектора или матрицы. Например, если подпрограмма должна обрабатывать вещественные массивы, не превышающие по размерности десяти элементов, в разделе описаний программы имеет смысл сделать следующее объявление: type vector = array [1..10] of real; Поскольку новый тип данных vector предназначен для использования подпрограммами, указанное выше объявление располагается до всех подпрограмм. В дальнейшем тип данных vector можно использовать как в разделе описания переменных var a,b:vector;, так и в списке формальных параметров функции или процедуры: function work (a:vector):real; Если по условию задачи в подпрограмме требуется при разных обращениях обрабатывать массивы различной размерности, то в разделе type объявляется тип массива с наибольшей из размерностей, встречающихся в задаче. Это чревато тем, что при обработке массивов меньшей размерности часть памяти останется неиспользованной. Кроме того, при обращении к подпрограмме в этом случае приходится отдельным параметром передавать фактическую размерность массива. Допустим, с помощью подпрограммы требуется найти максимальные элементы матриц A3x4, B4x3, C4x5. С учетом максимальной из имеющихся размерностей, делаем объявление типа matrix в разделе описаний: type matrix = array [1..4,1..5] of real;
Далее описываем конкретные матрицы как объекты нового типа данных matrix: var a,b,c:matrix; В качестве подпрограммы поиска значения максимального элемента матрицы напишем функцию max1 (функцию, а не процедуру, поскольку максимум -- скалярное значение). В эту функцию нам придется передать 2 дополнительных параметра-значения, определяющих количество строк n и количество столбцов m в конкретной матрице, переданной фактическим параметром: function max1 (n,m:integer; var a:matrix):real; var max:real; i,j:integer; begin max:=a[1,1]; for i:=1 to n do for j:=1 to m do if a[i,j]>max then max:=a[i,j]; max1:=max; end; Использование дополнительной локальной переменной max связано с теми же причинами, что в первом примере на функции. Обратите внимание, что, хотя функция max1 и не изменяет элементы своих матриц-параметров, в списке ее формальных параметров матрица указана как параметр-переменная. Для матриц и векторов это рекомендуется делать всегда, поскольку при указании параметра-переменной в матрицу передается только адрес того места памяти, где хранятся ее данные, а не копия всех элементов. В конечном счете, передача матриц и векторов формальным параметрам-переменным позволяет уменьшить размер генерируемого машинного кода. Вызвать нашу функцию для матриц A, B и C мы могли бы так: var ma,mb,mc:real; ... ma:=max1(3,4,a); mb:=max1(4,3,b); mc:=max1(4,5,c); Более удобную работу с массивами-параметрами предлагает механизм открытых массивов, рассмотренный в п. 18.4. Однако его непосредственно используют лишь для одномерных массивов. Пока же рассмотрим примеры решения типовых задач. 1. Заданы вектора A и B, содержащие по 5 элементов. Используя подпрограмму, найти их скалярное произведение по формуле Поиск скалярного произведения реализуем в виде подпрограммы-функции scal. type vector=array [1..5] of real;
function scal (n:integer; var a,b:vector):real; var i:integer; s:real; begin s:=0; for i:=1 to n do s:=s+a[i]*b[i]; scal:=s; end;
var i:integer; a,b:vector; s:real; begin writeln ('Вектор 1 из 5 элементов:');
for i:=1 to 5 do read (a[i]); writeln ('Вектор 2 из 5 элементов:'); for i:=1 to 5 do read (b[i]); s:=scal(5,a,b); writeln ('s=',s:10:3); end. 2. Сформировать по введенному с клавиатуры вектору A размерности n вектор res, компонентами которого являются отклонения элементов A от их арифметического среднего (подобная задача уже решалась выше, расширим ее на случай вектора). Задача предполагает написание, по меньшей мере, двух подпрограмм: функция Middle будет вычислять арифметическое среднее элементов вектора, а процедура Otkl – формировать по вектору A и ранее найденному среднему mid искомый вектор отклонений b. Компоненты вектора b при этом будут вычисляться по формуле . Поскольку о размерности векторов в задаче ничего не сказано, укажем в разделе type максимальную размерность, равную 100 элементам. type vector= array [1..100] of real;
function Middle (n:integer; var a:vector):real; var j:integer; res:real; begin res:=0.0; for j:=1 to n do res:=res+a[j]; Middle:=res/n; end;
procedure Otkl (n:integer; mid:real; var a,b:vector); var j:integer; begin for j:=1 to n do b[j]:=abs(a[j]-mid); end;
var a,res: vector; i,n:integer; s:real; begin write ('Размерность? '); readln (n); for i:=1 to n do begin write ('A[',i,']='); readln (a[i]); end; s:=Middle (n,a); Otkl (n,s,a,res); for i:=1 to n do writeln ('res[',i,']=',res[i]:8:2); end. 3. Используя подпрограмму, написать и проверить программу перемножения двух матриц. Как известно, матрица A размерностью n3m может быть умножена на матрицу B размерностью m3p по следующей формуле: где ci,j -- элемент получающейся в результате перемножения матрицы С размерностью n3m. Из формулы видно, что для умножения двух матриц нам понадобится тройной цикл: внешний цикл по i перебирает строки матрицы A, вложенный в него цикл по j выбирает в матрице B очередной столбец, а самый внутренний цикл по l умножает строку матрицы A на столбец матрицы B, получая элемент ci,j. Напишем соответствующую процедуру mmul и тестовую программу для нее: type matrix=array[1..10,1..10] of real;
var a,b,c: matrix; i,j,n,m,p: integer;
procedure mmul (n,m,k:integer; var a,b,c:matrix); var i,j,l:integer; s:real; begin for i:=1 to n do for j:=1 to k do begin s:=0; for l:=1 to m do s:=s+a[i,l]*b[l,j]; c[i,j]:=s; end; end;
begin repeat writeln; write ('Введите количество строк ', '1 матрицы: '); readln (n); write ('Введите количество столбцов ', '1 матрицы: '); readln (m); write ('Введите количество столбцов ', '2 матрицы: '); readln (p); until (n>1) and (n<11) and (m>1) and (m<11) and (p>1) and (p<11); for i:=1 to n do begin writeln ('Введите строку ',i,
' матрицы A из',m,'элементов:'); for j:=1 to m do read (a[i,j]); end; for i:=1 to m do begin writeln ('Введите строку ',i, ' матрицы B из',p,'элементов:'); for j:=1 to p do read (b[i,j]); end;
mmul (n,m,p,a,b,c); for i:=1 to n do begin writeln; for j:=1 to p do write (c[i,j]:10:3); end; end. 4. Процедурно ориентированные программы для распространенных задач решения системы линейных уравнений методом Гаусса, сортировки одномерного массива, поиска всех миноров второго порядка в квадратной матрице Вы можете найти и самостоятельно разобрать в Приложении 4. 5. В качестве еще одного развернутого примера на использование массивов в подпрограммах, разберем следующую задачу. Имеется N городов, между которыми налажены пассажирские перевозки. Между какими городами самый активный пассажиропоток? Количество городов обозначим константой cities. После математической формализации задачи, нетрудно заметить, что перевозки из города i в город j могут быть занесены в элемент матрицы ai,j, таким образом, требуется определить величину max(ai,j+aj,i), учитывая перевозки "туда" и "обратно". Для поиска максимального пассажиропотока достаточно двойного цикла с переменной границей по счетчику вложенного цикла. Как и в других программах, выделим в отдельные подпрограммы также типовые задачи ввода и вывода матрицы, а также ввода вещественного значения с контролем допустимых числовых границ ввода. const cities=10; type matrix=array [1..cities,1..cities] of integer;
function max1 (n:integer; var a:matrix; var imax,jmax:integer):integer; var i,j,m,p:integer; begin m:=a[1,2]; imax:=1; jmax:=2; for i:=1 to n do for j:=1 to n do if (i<>j) then begin p:=a[i,j]+a[j,i]; if p>m then begin m:=p; imax:=i; jmax:=j; end; end; max1:=p; end;
function readNumber (s:string; min,max:integer):integer; var a:integer; begin repeat write (s); {$I-}readln(a);{$I+} if IoResult<>0 then writeln ('Ошибка, введено не число!') else if (a<min) or (a>max) then writeln ('Ошибка, введенное число ', 'принадлежит интервалу [',min, ',', max, ']') else break; until false; readNumber:=a; end;
procedure readMatrix1 (var n:integer; var a:matrix); var i,j:integer; s,s1:string; begin n:=readNumber ('Введите число строк ', 'и столбцов матрицы: ',2,cities); for i:=1 to n do for j:=i+1 to n do begin s:='A['; str(i,s1); s:=s+s1+','; str(j,s1); s:=s+s1+']='; a[i,j]:=readNumber (s,0,maxInt); end; end;
procedure writeMatrix1 (s:string;
n:integer; var a:matrix); var i,j:integer; begin writeln (s); for i:=1 to n do begin for j:=1 to n do write (a[i,j]:7); writeln; end; end;
var a:matrix; n,gorod1,gorod2:integer;
begin readMatrix1 (n,a); max1 (n,a,gorod1,gorod2); writeMatrix1 ('A=',n,a); writeln ('Самый большой пассажиропоток ', 'между городами ',gorod1,' и ',gorod2); readln; end.
Открытые массивы
Недостатки изученного ранее способа передачи массивов-параметров очевидны: во-первых, необходимость описания типа данных массива оператором type нарушает правило переносимости подпрограмм (действие подпрограммы становится зависимым от внешнего оператора), во-вторых, для указания реальной размерности передаваемых в подпрограмму массивов приходится использовать дополнительные параметры-значения, в-третьих, при обработке массивов меньшей, чем указано в операторе type размерности, неэффективно теряется оперативная память. В какой то мере исправить эти недостатки позволяет использование открытых массивов. Способ подходит только для одномерных массивов. Использовать его с матрицами возможно, если интерпретировать матрицу как вектор (см. гл. 17). Имеющиеся в программе векторы описываются в разделе var обычным способом, без определения типа type. В списке формальных параметров подпрограммы параметр-вектор указывается без диапазона размерности: function sum(var x: array of real): real; При вызове подпрограммы фактический параметр-массив подставляется на место формального: var a:array [1..5] of real; s:real; ... s:=sum(a); Открытым остается вопрос -- как отслеживать из подпрограммы размерность переданного массива? Для этого в Паскале существуют стандартные функции Low и High. Их единственным параметром передается идентификатор массива, Low возвращает самое низкое допустимое значение индекса, а High -- самое высокое. Если A -- одномерный массив, величины Low(A) и High(A) можно непосредственно применять как границы цикла for: function sum(var x: array of real): real; var i:word; s:real; begin s:=0; for i:=Low(x) To High(x) Do s:=s+x[I]; sum:=s; end; Чтобы завершить пример, вызовем написанную функцию sum: const a:array [1..5] of real=(1,2,3,4,5.5); begin writeln (sum(a):6:1); end. Как правило, номер первого элемента открытого массива равен нулю, однако, надежнее все-таки указывать Low. Приведем пример программы, включающей подпрограмму с открытыми массивами в качестве параметров. Найти количество элементов вектора x[7], попадающих в интервал [0, 3] и количество элементов вектора y[5], попадающих в интервал [-1, 1]. Для ввода элементов массива с клавиатуры напишем процедуру Input, которой все-таки придется передавать размерность массива-параметра (ведь вводятся два вектора различной размерности). Поэтому в Input использован тот факт, что нумерация элементов открытого массива по умолчанию выполняется в нуля. Функции kol, вычисляющей количество элементов открытого массива, попадающих в интервал [x1,x2], достаточно стандартного указания Low и High:
var x:array [1..7] of real; y:array [1..5] of real; k1,k2,i:integer;
procedure Input (n:integer; var a:array of real); var i:integer; begin writeln ('Enter ',n,' items of array:'); for i:=0 to n-1 do read (a[i]); end;
function Kol (var a:array of real; x1,x2:real):integer; var i,k:integer; begin k:=0; for i:=Low(a) to High(a) do if (a[i]>=x1) and (a[i]<=x2) then k:=k+1; Kol:=k; end;
begin Input (7,x); Input (5,y); k1:=Kol(x,0,3); k2:=Kol(y,-1,1); writeln ('k1=',k1,' k2=',k2); end. Процедура Input могла бы быть реализована и без передачи фактической размерности отдельным параметром: procedure Input (var a:array of real); var i:integer; begin writeln ('Enter ',High(a)-Low(a)+1, ' items of array:'); for i:=Low(a) to High(a) do read (a[i]); end; {... } Input (x); Input (y);
Читайте также: III. Принцип дифференциации – интеграции, выступающий в качестве критерия развития структуры. Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|