Вопросы для проверки знаний.
. . .
SetLength(a,0); SetLength(b,0); {Удаление динамических массивов a и b}
end. {конец тела внешней программы}
Непосредственное применение открытых массивов рассчитано на одномерные массивы. Однако, как и в случае динамических массивов, данные средства могут быть использованы и для передачи многомерных массивов. Одним из наиболее простых путей решения данной задачи является применение взаимно однозначного отображения многомерных массивов на соответствующие одномерные для передачи их в подпрограммы в виде открытых массивов. Для интерпретации передаваемого открытого массива как многомерного в подпрограмму дополнительно надо передать размерность по отдельным координатам.
Пример 4. Разработать программу, в которой выполнялись бы следующие действия:
а) ввод с клавиатуры числа строк n и столбцов m целочисленной матрицы M[1..n,1..m],
б) ввод с клавиатуры элементов матрицы M[1..n,1..m],
в) формирование с помощью отдельной процедуры SUM_STRвектора S[1..n], состоящего из сумм элементов в строках матрицы,
г) вывод вектора S[1..n]на экран,
д) удаление всех динамических массивов программы.
Решение. Обозначим номера строк и столбцов матрицы через i и j, динамический линейный массив, соответствующий ей, через LM. Функция k = k (i, j, m)для расчета номеров коэффициентов LM по коэффициентам матрицы и числу столбцов взята из примера 5 п.8.5. Число столбцов матрицы m передается в процедуру суммирования элементов строк SUM_STR по двум причинам: для использования в функции k (i, j, m) и для расчета числа строк n (чтобы не передавать его через заголовок процедуры). При задании номеров элементов необходимо учитывать, что в матрице начало отсчета индексов с 1, а в динамических массивах – с 0.
var LM: array of integer;{ Описание дин. массиваLM}
S: array of integer;{ Описание дин. массиваS}
n,m,i,j,el: integer;
function k(i,j,m:integer):integer; {функция для расчета k = k (i, j, m) = (i-1)m +(j-1) }
begin k:= (i-1)*m + j -1; end;
procedure SUM_STR(L:array of integer; m:integer; var STR_S:array of integer);
{процедура для расчета вектора из сумм элементов в строках матрицы }
var k,n,i_first,i_fin,i,j:integer;
begin{начало тела процедуры SUM_STR }
i_first:=Low(L);i_fin:=High(L);{определение границ массиваL}
n:=(i_fin-i_first+1) div m;{определение количества строкn в матрице, соответствующей динамическому массиву L}
for i:=1 to n do STR_S[i-1]:=0;{начальная инициализация массива STR_S нулями}
for i:=1 to n do{расчет элементов массива STR_S}
for j:=1 to m do
begin k:=(i-1)*m+j-1; STR_S[i-1]:=STR_S[i-1]+L[k] end;
end; {конец тела процедуры SUM_STR }
begin{ начало тела основной программы}
writeln (' Vvedite сhislo strok:');read(n);{запрос и ввод числа строк n}
writeln (' Vvedite сhislo stolbtsov:');read(m);{запрос и ввод числа строк m}
SetLength (LM,n*m);{Создание динамического массиваLMдля ввода матрицыM[1..n,1..m] }
SetLength (S,n);{Создание динамического массиваSдля сумм элементов в столбцах матрицы }
for i:=1 to n do{ввод элементов матрицы с клавиатуры}
for j:=1 to m do begin
writeln('vvedite element M[',i,',',j,']=');{запрос на ввод элемента M[i,j]}
read(el); LM[k(i,j,m)]:=el;{ввод элемента M[i,j] в линейный массив LM }
end;
for i:=1 to n do begin{вывод матрицы M[1..n,1..n] на экран}
for j:=1 to m do write(' M[',i,',',j,']=',LM[k(i,j,m)]:6);{вывод строки}
writeln;{переход на очередную строку}
end;
SUM_STR(LM, m, S); {вызов процедуры SUM_STR }
for i:=0 to n-1 do{Печать динамического массиваS }
writeln(' Sum of elements in stroke ',i+1,'=',S[i]:6);
SetLength(LM,0); SetLength(S,0); //Удаление динамических массивов LM и S
end. {конец тела основной программы}
Таким образом, рассмотрены два способа передачи массивов в подпрограммы: 1) собъявлением (описанием) типа массива в вызывающем блоке и 2) с использованием открытых массивов. Применение открытых массивов позволяет создавать подпрограммы, обрабатывающие линейные массивы различной длины без дополнительного описания типа массивов в вызывающем блоке и передачи длины массива в качестве параметра подпрограммы. Через входной параметр, являющийся открытым массивом, в подпрограмму могут быть переданы как статические, так и динамические массивы. Для передачи через открытый массив многомерных массивов переменных размеров могут быть использованы те же методы, что и для организации многомерных динамических массивов.
1. Как передают в подпрограммы статические массивы постоянного размера с использованием описания их типа в вызывающем блоке ?
2. Как передают в подпрограммы статические массивы ограниченного размера с использованием описания их типа в вызывающем блоке ?
3. Где и с какой целью применяют открытые массивы ?
4. Какие ограничения снимает использование открытых массивов в случае передачи в подпрограммы массивов различного размера ?
5. Какие ограничения снимает передача через открытые массивы динамических массивов по сравнению со статическими ?
6. Можно ли использовать открытые массивы для передачи многомерных динамических массивов переменных размеров ?
Практические задания.
1. Разработать варианты а) - г) программы, в которой производится ввод с клавиатуры двух векторов`a = (a1,...,an) и`b = (b1,...,bn) и последующий расчет их скалярного произведения (`a ,`b) = (a1b1 +...+anbn) с помощью отдельной функции Sc(`a ,`b ):
а) с использованием для векторов`a и`b статических массивов постоянной длины n=3 и опиcания типа массива в вызывающем блоке;
б) с использованием для векторов`a и`b статических массивов переменной длины 1 ≤ n ≤ 100 и опиcания типа массива в вызывающем блоке;
в) с использованием для векторов`a и`b статических массивов переменной длины 1 ≤ n ≤ 100 и открытых массивов в заголовке кода функции Sc(`a ,`b );
г) с использованием векторов`a и`b произвольной длины n, динамических массивов в вызывающем блоке и открытых массивов в заголовке кода функции Sc(`a ,`b ).
Пока же рассмотрим примеры решения типовых задач.
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.