Многомерные массивы
Можно объявлять и многомерные массивы, т.е. массивы, элементами которых являются массивы. Например, двумерный массив можно объявить таким образом:
Var
A2 : array [1..10] of array [1..3] of integer;
Этот оператор описывает двумерный массив, который можно представить себе как таблицу, состоящую из 10 строк и 3 столбцов. То же самое можно объявить более компактно:
Var A2: array[l..10,1. .3] of integer;
Обычно используется именно такая форма объявления многомерных массивов. Как и в одномерных массивах, элементы могут иметь любой тип и индексы тоже могут иметь любой ограниченный тип.
Доступ к значениям элементов многомерного массива обеспечивается через индексы, перечисляемые через запятую. Например, А2[4,3] — значение элемента, лежащего на пересечении четвертой строки и третьего столбца.
Так же, как и в случае одномерных массивов, можно определять не непосредственно переменные типа многомерных массивов, а сначала определять соответствующий тип, а затем — переменные или типизированные константы этого типа.
Например:
type
Ar3 = array[1. . 4 , 1. .3, 1. .2] of integer;
var
A1,A2: Ar3;
const
A3: Ar3=( ( (0,1) , (2, 3), (4, 5) ) ,
( ( 6 , 7 ) , (8, 9), (10,11)),
( ( 12 , 13 ) , (14,15), (16,17)),
( ( 18 , 19 ) , (20,21), ( 22, 23 ) ) ) ;
При задании начальных условий список значений по каждой размерности заключается в скобки. Приведенный выше пример типизированной константы создает массив A3, четыре строки которого являются матрицами вида
0 1 6 7 12 13 18 19
2 3 8 9 14 15 20 21
4 5 10 11 16 17 22 23
Например, элемент A3[1,2,1] равен 2, элемент А3[4,1,2] равен 19 и т.д.
Операции с массивами, передача массивов как параметров
Для массивов одного типа определена операция присваивания. Например, если массивы объявлены как
var A,B: array[1..3] of integer;
то в результате выполнения оператора
А := В;
значения элементов массива В присвоятся соответствующим элементам массива А.
Прежние значения элементов А будут затерты. Таким образом, произойдет копирование В в А. Сами массивы при этом останутся самостоятельными и в дальнейшем их элементы могут изменяться независимо друг от друга.
Аналогично будет работать оператор присваивания, если переменные массивов объявлены следующим образом:
type
Ar = array[1..3] of integer;
var
A: Ar;
B: Ar;
Ho если объявить массивы следующим образом:
var
A: array[1..3] of integer;
В: array[1..3] of integer;
то при попытке присваивания А:=В компилятор выдаст синтаксическую ошибку с сообщением ‘Incompatible types’ — несовместимые типы. Дело в том, что компилятор считает, что переменные имеют один тип только в случаях, если они явным образом определены через некоторый поименованный тип, как сделано во втором примере, или если они объявлены в одном списке, как это имеет место в первом примере. А в третьем примере ни то, ни другое условие не соблюдается.
Для любого массива определены следующие функции:
|
Length |
число элементов массива |
|
High |
наибольшее значение индекса |
|
Low |
наименьшее значение индекса |
Впрочем, для рассматриваемых массивов использовать эти функции вряд ли имеет смысл, поскольку все эти значения известны из объявления массива. Они используются для открытых и динамических массивов, о которых будет сказано позднее.
Для числовых массивов определены функции:
|
Функция
|
Тип аргумента |
Тип результата
|
Описание
|
|
MaxIntValue
|
array of integer
|
integer
|
возвращает максимальное значение элемента массива целых чисел |
|
MinIntValue
|
array of integer
|
integer
|
возвращает минимальное значение элемента массива целых чисел |
|
MaxValue
|
array of double
|
double
|
возвращает максимальное значение элемента числового массива |
|
MinValue
|
array of double
|
double
|
возвращает минимальное значение элемента числового массива |
|
Sum
|
array of double
|
extended
|
возвращает сумму элементов массива |
Эти функции определены в модуле math и этот модуль должен подключаться оператором uses, чтобы компилятор их понимал.
К символьным массивам применимы функции работы со строками.
Если вы хотите создать свою функцию или процедуру работы с массивами, то надо иметь в виду, что в ее объявление нельзя включать описание индексов. Например, объявление
procedure MyProc(A: array[1..10] of Integer);
будет расценено как синтаксическая ошибка и вызовет соответствующее сообщение компилятора. Правильным будет объявление
type
ta = array[l..10] of Integer;
procedure MyProc(A: ta) ;
Функции и процедуры в Object Pascal могут воспринимать в качестве параметров не только массивы фиксированного размера, но и так называемые открытые массивы, размер которых неизвестен. В этом случае в объявлении функции или процедуры они описываются как массивы базовых типов без указания их размерности.
В качестве примера использования открытых массивов рассмотрим процедуру, которая принимает два открытых одинакового размера массива целых чисел, суммирует их и заносит результат во второй из переданных массивов. Заголовок этой процедуры:
procedure SumArray(A:array of integer;
var B: array of integer);
При таком определении передаваемый в функцию первый массив будет копироваться и с этой копией - массивом A, будет работать процедура. Второй открытый массив определен как var. Этот массив передается по ссылке, т.е. он не копируется и процедура будет работать непосредственно с исходным массивом.
procedure SumArray(A:array of integer;
var B: array of integer);
var i:word;
begin
for i:=0 to High(A) do B[i]:=A[i]+B[i];
end;
Вызов этой процедуры может иметь вид:
var A1,A2: array [1..3] of integer;
begin
<операторы заполнения массивов>
SumArray(A1,A2);
end;
Обратите внимание на то, что массивы A1 и A2, передаваемые в качестве аргументов, имеют значения индексов от 1 до 3, а процедура оперирует с индексами в диапазоне 0 - 2. Однако никакой путаницы не возникнет. Просто, например, элемент A[0] в теле процедуры будет соответствовать элементу A1[1] в массиве A1.
Массив, переданный как открытый, воспринимается в теле процедуры или функции как массив с целыми индексами, начинающимися с 0. Размер массива может быть определен функциями Length - число элементов и High - наибольшее значение индекса. Очевидно, что всегда High = Length - 1.
При вызове функции или процедуры с параметром в виде открытого массива можно использовать в качестве аргумента конструктор открытого массива, который формирует массив непосредственно в операторе вызова. Список элементов такого конструктора массива заключается в квадратные скобки, а значения элементов разделяются запятыми. Например, функцию Sum, суммирующую элементы числового массива, можно вызвать следующим образом:
Sum([1.2,4.45,0.1]);
Пример использования открытых массивов для заполнения одномерных массивов, вывода их в компоненты Memo, суммирования элементов одномерного массива целых чисел, использования функций суммирования и поиска максимального элемента из модуля math.
Двумерный открытый массив объявлять нельзя.
type
TarrInt=array [1..3,1..5] of integer;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Memo2: TMemo;
Memo3: TMemo;
Panel1: TPanel;
Label1: TLabel;
Memo4: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
procedure InputOpenArrInt(var A : array of integer);
procedure OutputOpenArrInt(var A : array of integer);
procedure OutputOpenArrFloat(var A : array of double);
procedure InputOpenArrFloat(var A : array of double);
function SumOpenArrInt(A : array of integer):integer;
procedure InputArr(var A :TarrInt);
procedure OutputArr(var A :TarrInt);
function SumArr(var A :TarrInt): integer;
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var
a : array[1..5] of integer;
c : array[1..3] of double;
b : TarrInt;
A2: array of array of integer;
{$R *.dfm}
procedure TForm1.InputOpenArrInt(var A : array of integer);
var
i : integer;
begin
randomize;
for i:=0 to high(A) do // Можно for i:=low(A) to high(A) do
A[i]:=random(11);
end;
procedure TForm1.OutputOpenArrInt(var A : array of integer);
var
i : integer;
begin
for i:=0 to high(A) do
memo1.Lines.Add(intToStr(A[i]));
end;
procedure TForm1.InputOpenArrFloat(var A : array of double);
var
i : integer;
begin
randomize;
for i:=low(A) to high(A) do A[i]:=random;
end;
procedure TForm1.OutputOpenArrFloat(var A : array of double);
var
i : integer;
begin
for i:=0 to high(A) do
memo3.Lines.Add(floatToStr(A[i]));
end;
function TForm1.SumOpenArrInt(A : array of integer):integer;
var
i: integer;
begin
result:=0;
for i:=0 to high(A) do
result:=result+a[i];
end;
procedure TForm1.InputArr(var A :TarrInt);
var
i,j : integer;
begin
randomize;
for i:=1 to 3 do
for j:=1 to 5 do
B[i,j]:=random(11);
end;
procedure TForm1.OutputArr(var A :TarrInt);
var
i,j : integer;
str : string;
begin
for i:=1 to 3 do
begin
str:='';
for j:=1 to 5 do
str:=str+intToStr(B[i,j])+' ';
memo2.lines.Add(str);
end;
end;
function TForm1.SumArr(var A :TarrInt): integer;
var
i,j : integer;
begin
result:=0;
for i:=1 to 3 do
for j:=1 to 5 do
result:=result+B[i,j];
end;
//нельзя объявлять двумерный открытый массив!!!
// function SumB(b :array of array of integer): integer;
// Ошибка : ERROR Identifier expected but 'ARRAY' found
procedure TForm1.Button1Click(Sender: TObject);
var
i,j,N, m : integer;
str : string;
s: integer;
begin
InputOpenArrInt(A);
OutputOpenArrInt(A);
memo1.Lines.Add('S='+intToStr(SumOpenArrInt(A)));
memo1.Lines.Add('Max='+ intToStr(MaxIntValue(A))); //MaxIntValue - стандартная ф-ция,
// описанная в моделе math
InputOpenArrFloat(C);
OutputOpenArrFloat(C);
memo3.Lines.Add('S='+floatToStr(Sum(C))); //Sum - стандартная ф-ция,
// описанная в моделе math
InputArr(B);
OutputArr(B);
memo2.Lines.Add('S='+intToStr(SumArr(B)));
end;
end.
Открытые массивы специального вида, объявляемые как array of const, разрешают, в отличие от всех других массивов, передавать в процедуру или функцию массив различных по типу значений.
Такой массив является массивом структур типа TVarRec. Эти структуры могут содержать значения типов целых чисел, действительных чисел, булевские значения, символы, строки, указатели, классы и многое другое. Структуры имеют поле VType, указывающее тип каждого элемента массива. Передача некоторых типов, например, длинных строк, производится по ссылке через соответствующие указатели. Другие типы могут передаваться по значению.
Динамические массивы
В версии Delphi 4 впервые введены так называемые динамические массивы. При объявлении таких массивов в программе не следует указывать границы индексов:
var
A: array of Integer;
В: array of array of Char;
C: array of array of array of Real;
В этом примере динамический массив А имеет одно измерение, массив В - два и массив С - три измерения. Распределение памяти и указание границ индексов по каждому измерению динамических массивов осуществляется в ходе выполнения программы путем инициации массива с помощью функции setLength. В ходе выполнения такого оператора:
SetLength(А,3);
одномерный динамический массив а будет инициирован, т. е. получит память, достаточную для размещения трех целочисленных значений. Нижняя граница индексов по любому измерению динамического массива всегда равна 0, поэтому верхней границей индексов для А станет 2.
Фактически идентификатор динамического массива ссылается на указатель содержащий адрес первого байта памяти, выделенной для размещения массива. Поэтому для освобождения этой памяти достаточно присвоить идентификатору значение nil (другим способом является использование процедуры Finalize):
var
А,В: array of Integer;
begin
// Распределяем память:
SetLength(A,10) ;
SetLength(B,20) ;
// Используем массивы:
// Освобождаем память:
А := NIL;
Finalize(В);
end;
При изменении длины уже инициированного динамического массива по какому-либо его измерению сначала резервируется нужная для размещения нового массива память, затем элементы старого массива переносятся в новый, после чего освобождается память, выделенная прежнему массиву. Чтобы сократить дополнительные затраты времени, связанные с изменением границ большого динамического массива, следует сразу создать массив максимальной длины.
В многомерных массивах сначала устанавливается длина его первого измерения, затем второго, третьего и т. д.
Один из способов отвести память под такой массив - передать в процедуру SetLength в качестве параметров несколько размеров. Например
var
A: array of array of Integer;//Двумерный динамический массив
begin
SetLength(A,3,4);
задает размерность массива 3 на 4.
Можно задавать размерность для каждого столбца отдельно. Например:
var
A: array of array of Integer;//Двумерный динамический массив
begin
//Устанавливаем длину первого измерения (количество строк):
SetLength(A,3) ;
//Задаем длину каждой строки:
SetLength(A[0],3) ;
SetLength(A[l],3) ;
SetLength(A[2] ,3) ;
end;
Обратите внимание: в отличие от обычных массивов стандартного Паскаля (и Object Pascal), динамические массивы могут иметь разную длину по второму и следующим измерениям. В предыдущем примере определен квадратный массив 3х3. Однако ничто не мешает нам создать, например, треугольный массив:
SetLength(A,3) ;
//Задаем длину каждой строки:
SetLength(A[0],3) ;
SetLength(A[l],4) ;
SetLength(A[2],5) ;
В многомерных динамических массивах каждый элемент любого из N-1 измерений (N - количество измерений) представляет собой динамический массив и, следовательно, нуждается в инициации. Вот как, например, можно инициировать вещественный кубический массив 3х3х3:
var
A: array of array of array of Real;
i, j: Integer;
begin
SetLength(A,3) ;
for i := 0 to 2 do
begin
SetLength(A[i],3) ;
for j := 0 to 2 do
SetLength(A[i,j],3) ;
end;
end;
Пример. Рассмотрим программу построения и заполнения нижней треугольной матрицы произвольного размера N.
var A2: array of array of integer;
N,i1,i2,m: integer;
begin
N:=3;
m:=1;
SetLength(A2,N); {Задание числа строк = N}
for i:=0 to N-1 do {Цикл по строкам}
begin
s:='';
SetLength(A2[i],i+1); {Число столбцов равно номеру строки}
for j:=0 to i do
begin
A2[i,j]:=m; {Заполнение строки}
Inc(m); {Увеличение m на 1}
s:=s+IntToStr(a2[i,j])+' ';
end;
memo1.lines.Add(s);
end;
Программа формирует двумерный массив, в котором число строк равно значению N, а число столбцов в каждой строке равно номеру строки.
Пример (приведен в файле 'Тема 6 Таблицы строк.doc') считывания и записи матрицы из/в компонент StringGrid. На форме расположены 3 компонента StringGrid: strgrdA, strgrdB, strgrdResult; 2 кнопки btnRead и btnWrite. При нажатии на кнопку btnRead выполняется считывание 2-х массивов из компонент StringGrid .
type
TMatrix = array of array of integer;
TfrmMatrices = class(TForm)
strgrdB: TStringGrid;
strgrdA: TStringGrid;
strgrdResult: TStringGrid;
btnRead: TButton;
btnWrite: Tbutton;
procedure btnRead(Sender: TObject);
procedure btnWrite(Sender: TObject);
private
{ Private declarations }
procedure ReadMatrix(var fg: TMatrix; StringGrid: TStringGrid);
procedure WriteMatrix(var fg: TMatrix; StringGrid: TStringGrid);
public
{ Public declarations }
end;
var
frmMatrices: TfrmMatrices;
A, B: TMatrix;
implementation
{$R *.dfm}
procedure TfrmMatrices.ReadMatrix(var fg: TMatrix; StringGrid: TStringGrid);
var
i, j: integer;
begin
SetLength(fg, StringGrid.RowCount, StringGrid.ColCount);
with StringGrid do
begin
for i:= 0 to RowCount-1 do
for j:= 0 to ColCount-1 do
begin
fg[i, j] := StrToInt(Cells[j, i]);
end;
end;
end;
procedure TfrmMatrices.WriteMatrix(var fg: TMatrix; StringGrid: TStringGrid);
var
i, j: integer;
begin
StringGrid.ColCount := Length(fg[0]);
StringGrid.RowCount := Length(fg);
for i:= 0 to Length(fg)-1 do
for j:= 0 to Length(fg[i])-1 do
StringGrid.Cells[j, i] := IntToStr(fg[i, j]);
end;
procedure TfrmMatrices.btnRead(Sender: TObject);
begin
try
ReadMatrix(A, strgrdA);
ReadMatrix(B, strgrdB);
except
on EConvertError do
begin
MessageDlg('Ошибка конвертирования. В поля можно вводить только числа.' +
#13#10 + 'Все ячейки должны быть заполнены', mtError, [mbOK], 0);
exit;
end;
end;
Задание. Напишите обработчик нажатия кнопки btnWrite в котором выведите элементы массива А в StringGrid.
