Практически для каждого языка программирования создаются пакеты прикладных программ (ППП), реализующих часто встречающиеся процедуры численного анализа (решение нелинейных уравнений, нахождение корней полиномов, обращение матрицы, решение системы дифференциальных уравнений и т.п.). Прикладная программа разрабатывается в виде подпрограммы, обращение к которой производится из программы пользователя. При этом, как правило, ППП поставляется в виде объектных модулей, исключающих возможность изменения их текста пользователем.
Паскаль-подпрограмма, обрабатывающая массив, должна содержать в списке формальных параметров имя этого массива с указанием соответствующего имени типа. В общем случае имя типа формального массива не совпадает с именем типа фактического массива в программе пользователя. Cледовательно, прикладная подпрограмма должна обеспечивать совместимость типов формального и фактического массивов.
В Паскаль-программе массив всегда имеет фиксированный размер, определяемый его именем типа. Необходимость использования различных имен типов связана главным образом с тем, что формальный и фактический массивы в общем случае имеют различные размеры. Типы элементов этих массивов, естественно, должны быть одинаковыми.
Пример 1. Предположим, что для массивов X, Y и Z, имеющих различные имена типов, необходимо вычислить среднее арифметическое их элементов. Тогда программа может иметь следующий вид.
Program Middle1;
Type Xar = array[1..50] of real;
Yar = array[1..500] ofreal;
Zar = array[1..5000] of real;
Var i,nx,ny,nz : integer;
Sx,Sy,Sz : real;
X : Xar;
Y : Yar;
Z : Zar;
ProcedureMiddleAr(Var Buf:Xar; Var S:real; n:integer);
Var i : integer;
Begin
S:=0;
For i:=1 to n do
S:=S+Buf[i];
S:=S/n;
End { MiddleAr };
Begin
Ввод и печать nx, ny, nz, X, Y, Z
MiddleAr(X,Sx,nx);
MiddleAr(Y,Sy,ny);
MiddleAr(Z,Sz,nz);
Печать Sx, Sy, Sz
End.
Здесь при трансляции программы будет правильно воспринято лишь первое обращение к процедуре MiddleAr; для остальных обращений будет выдано сообщение о несоответствии типов фактических параметров Y, Z и формального параметра Buf.
Обработка массивов различного размера (более точно, массивов с разными именами типов) в Турбо Паскале может быть обеспечена путем использования аппарата приведения типов переменных или с помощью абсолютных переменных.
В связи с тем, что фактический параметр-массив при разных обращениях к процедуре может иметь различные имена типов, то ему не может соответствовать ни один формальный параметр с конкретным именем типа. Для обеспечения совместимости типов в процедуре должен указываться формальный параметр без типа. Такому формальному параметру могут соответствовать фактические параметры любого типа.
Формальному параметру без типа имеет смысл передавать лишь адрес, а не значение фактической переменной, поскольку адрес переменной всегда имеет постоянную длину (4 байта), а значение переменной имеет длину, зависящую от типа этой переменной. Следовательно, формальный параметр без типа обязательно должен иметь слово Var в заголовке процедуры (это так называемый нетипизированный параметр-переменная).
С помощью аппарата приведения типа в процедуре можно организовать обработку массивов с различными именами типов, т.е. массивов, имеющих в общем случае различный размер. При этом элементы указанных массивов должны быть, естественно, одного и того же типа. В частности, программа Middle1 тогда может иметь следующий вид:
Хотя "интерпретирующее" поле RealAr, накладываемое на формальную переменную Buf без типа, длиннее фактических переменных X, Y или Z, в процедуре MiddleAr выхода за пределы реальных массивов не произойдет, если переменные nx, ny, nz не превышают соответственно значений 50, 500, 5000.
Более предпочтительным для RealAr является объявление вида
RealAr = array[1..(2*MaxInt) div SizeOf(real)] of real.
Здесь компилятор определяет для RealAr максимально возможный размер, самостоятельно вычисляя количество элементов такого массива ( 2 × 32767 div6 = 10992 ).
Тип RealAr в процедуре MiddleAr накладывается на формальный параметр Buf без имени типа, который при обращении к процедуре заменяется именем фактического массива. Так как RealAr - это имя типа, а не описание переменной, то объекту с именем RealAr никакой памяти не выделяется. Как и любое имя типа, RealAr определяет множество значений, которые может принимать переменная с этим именем типа.
Как уже было отмечено, для формального параметра Buf в процедуре MiddleAr не указано никакого имени типа. Если бы в этой процедуре было записано S := S + Buf[i], то транслятор выдал бы сообщение об ошибке, поскольку в списке формальных параметров нет информации о том, что собой представляет идентификатор Buf (простая переменная, массив и т.п.). Можно было бы возразить, что эту информацию транслятор мог бы получить из обращения к процедуре MiddleAr. Но ведь описание процедуры предшествует разделу операторов. Следовательно, при трансляции этой процедуры еще не прочитаны обращения к ней, и тип переменной Buf остается неопределенным.
Запись приведенного выше оператора в форме S := S + RealAr(Buf)[i] снимает такое недоразумение, поскольку транслятору в этом случае указано, что имя Buf следует рассматривать как имя одномерного массива с вещественными компонентами.
Абсолютные переменные можно использовать в процедурах для обработки массивов с разными именами типов аналогично аппарату приведения типов.
Пример 2. В одномерном целочисленном массиве удалить повторяющиеся элементы, оставляя в составе массива лишь первое включение таких элементов. Программу решения задачи оформить в виде процедуры, обрабатывающей массивы с различными именами типов.
Вначале составим отдельную программу обработки одного массива.
Для заданного массива X будем использовать буферный массив Y, в который пересылаются те элементы массива X, которые еще не записаны в массив Y.
Program DelElem;
Const Nmax = 500;
TypeAr = array[1..Nmax] of integer;
Var X,Y : Ar;
i, { параметр цикла }
n, { реальный размер массива X }
m : word; { реальный размер массива Y }
{ ---------------------------------------- }
FunctionFindElem(r:integer):boolean;
{ Определение признака включения элемента в массив Y }
Var i : word;
Begin
FindElem:=false;
Fori:=1 tom do
If r=y[i] then
Begin
FindElem:=true; Exit
End;
End{ FindElem };
{ ---------------------------------------- }
Begin
Ввод и печать n, X
m:=0;
For i:=1 to n do
If not FindElem(x[i]) then{ элемент x[i] записывается }
Begin{ в массив Y, если в этом }
Inc(m); y[m]:=x[i] { массиве еще нет такого }
End;{ элемента }
X:=Y; n:=m;
Печать n,X
End.
В программе DelElem создается буферный (временный) массив Y, необходимый лишь на период обработки массива X в соответствии с использованным здесь алгоритмом. Размер массива Y в соответствии с его объявлением равен размеру массива X (учитывается частный случай, когда в массиве X нет одинаковых элементов). Если программу DelElem оформлять в виде процедуры, то массив Y должен создаваться в процессе работы этой процедуры, т.е. переменная Y должна быть локальной. Но, как было оговорено в условии задачи, процедура DelElem должна обрабатывать массивы с различными именами типов, имеющими в общем случае различные размеры. Поскольку локальный массив, объявляемый в процедуре, должен иметь вполне определенное, заранее указанное в тексте программы, имя типа, то обеспечить размер такого массива равным размеру обрабатываемого массива X, невозможно. Следовательно, алгоритм, использованный в программе DelElem, не позволяет оформить эту программу в виде процедуры для обработки массивов с различными именами типов. В данном случае необходимо разработать такой алгоритм решения задачи, для реализации которого не требуется дополнительный буферный массив.
В процедуре DelElems, приведенной ниже, производится последовательный просмотр элементов массива A, совмещенного по адресу с формальным массивом U, и, в случае обнаружения повторяющегося элемента, производится его удаление путем сдвига оставшейся части массива на один элемент влево.