Массивы в Delphi

Массивы чрезвычайно полезны при работе с большим количеством логически связанных между собой переменных одного типа. Например, представьте, что необходимо создать приложение для местного кинотеатра, которое отображает занятые и свободные места в зале. Предположим, что зал кинотеатра имеет 350 мест. Для запоминания состояния каждого места потребовалось бы 350 булевских переменных с уникальными именами:

1.var 2.seatl: Boolean; 3.seat2: Boolean; 4.seat3: Boolean; 5.seat350: Boolean;
 

Мало того, что столь большое количество объявлений совершенно неприемлемо, такой подход порождает множество других проблем. Что делать, если требуется обновить состояние места в зависимости от информации, вводимой пользователем? Единственный способ решения этой задачи — использование огромного количества операторов if-then или case, которые должны были бы проверять введенное пользователем значение и изменять соответствующую переменную. Это означает, что для обеспечения простого изменения состояния только одного места пришлось бы написать, по меньшей мере, 350 строк кода:

 

01.var
02.seatl: Boolean;
03.seat2: Boolean;
04.seat3: Boolean;
05.seat350: Boolean; userSeat: Integer;
06.begin
07.ReadLn(userSeat);
08.case userSeat of
09.1 : seatl := True;
10.2: seat2 := True;
11.350: seat350 := True; end;
12.end.

 

 

Очевидно, что должен существовать более рациональный способ работы с боль­шими коллекциями переменных. Лучший метод выполнения подобных задач свя­зан с использованием массива.

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

Синтаксис объявления массива выглядит следующим образом:

ИмяМассива: array[Диапазон] of ТипДанных; Cinema: array[1..350]  of Boolean;

Чтобы обратиться к переменной в массиве, необходимо указать имя массива, за которым должен следовать индекс переменной. Индекс переменной должен быть заключен в квадратные скобки. Синтаксис обращения к отдельному элементу мас­сива показан ниже:

ИмяМассива [Индекс]

Так, например, если мы используем массив Cinema, и нам необходимо изме­нить состояние места номер 265 на свободное, можно записать следующую строку кода:

Cinema[265]   : = False; Индекс элемента не обязательно должен быть постоянным значением. Он мо­жет быть переменной, а также результатом выполнения функции. Важно помнить, что значение индекса должно лежать в пределах объявленного диапазона. 

Листинг 6.1. Обращение к элементу массива 

  

01.program Project1;
02.{$APPTYPE CONSOLE}
03.uses
04.SysUtils;
05.const
06.MAX_SEATS = 350;
07.var
08.Cinema: array[1..MAX_SEATS] of Boolean;
09.userSeat: Integer;
10.begin 11.ReadLn(userSeat);
12.if (userSeat >= 1) and (userSeat <= MAX_SEATS) then
13.Cinema[userSeat] := True;
14.end.

 

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

Применительно к рассматриваемому примеру легко установить все места сво­бодными, если кинотеатр пуст, или вычислить процент занятых мест. Если вни­мательнее ознакомиться с функцией Get Percent, легко заметить, что массивы поддерживают новый вид цикла for-in.

Листинг 6.2. Обращение к элементам массива в цикле 

 

01.program Project1;
02.{$APPTYPE CONSOLE}
03.uses
04.SysUtils;
05.const
06.MAX_SEATS = 350;
07.var
08.Cinema: array[1..MAX_SEATS] of Boolean;
09.i: Integer; 10.procedure ClearCinema;
11.var
12.i: Integer;
13.begin
14.for i := 1 to MAX_SEATS do
15.Cinema := False;
16.end;
17.function GetPercent: Double;
18.var
19.seat: Boolean;
20.occupiedCnt: Integer;
21.begin
22.occupiedCnt := 0;
23.for seat in Cinema do
24.if seat then Inc(occupiedCnt);
25.Result := occupiedCnt * 100 / MAX_SEATS;
26.end;
27.begin
28.{ 67 seats occupied }
29.for i := 1 to 67 do
30.Cinema := True;
31.WriteLn(GetPercent:0:2, '% seats occupied.');
32.ReadLn;
33.end.

 

В Delphi массивы предоставляют значительно больше возможностей, чем массивы в других языках программирования. Например, индексы массивов в С. С# и VB.NET всегда начинаются с нуля. В Delphi можно использовать значения индексов, которые наиболее подходят для конкретного случая:

 

1.negativeArray: array[-100100] of Integer;
2.biglndex: array[10001020] of Integer;
3.alphabet: array[Ord(' a')..Ord('z1)]  of Char;

 

Поскольку в Delphi массивы могут иметь различные индексы, запоминание начального и конечного индекса каждого используемого в приложении массива может представлять трудность. В Delphi реализованы две функции, которые извлекают индексы первого и последнего элементов массива: Low и High. Если цикл f or-in не используется, наилучшим способом считывания значений индексов будет использование функций Low и High.  Листинг 6.3. Функции Low и High 
01.program Project1;
03.{$APPTYPE CONSOLE}
05.uses
06.SysUtils;
08.var
09.alphabet: array[Ord('a')..Ord('z')] of Char;
10.i: Integer;
12.begin
13.for i := Low(alphabet) to High(alphabet) do
14.begin 15.alphabet := Chr(i);
16.WriteLn(alphabet);
17.end; 
19.ReadLn;
20.end.
Функции Low и High избавляют программиста от бремени запоминания точных индексов массива, легко читабельны и не замедляют работу приложения, поскольку они не вызываются во время выполнения. Компилятор заменяет вызовы обеих этих функций соответствующими значениями из объявления массива во время компиляции.

Поскольку в Delphi массивы могут иметь различные индексы, запоминание начального и конечного индекса каждого используемого в приложении массива может представлять трудность. В Delphi реализованы две функции, которые извлекают индексы первого и последнего элементов массива: Low и High. Если цикл f or-in не используется, наилучшим способом считывания значений индексов будет использование функций Low и High.

Листинг 6.3. Функции Low и High

 

01.program Project!;
02.1$APPTYPE CONSOLE}
03.uses
04.SysUtils;
05.var
06.alphabet: array[Ord('a1)..Ord('z1)] of Char; i: Integer;
07.begin
08.for i :» Low(alphabet)  to High(alphabet) do begin
09.alphabet   := Chr(i); WriteLn(alphabet[i1); end;
10.ReadLn;

 

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

 

1.ИмяМассива: array[Диапазон] of ТипДанных =  (значение_1,значение_2, значение_n) ;

 

В ряде случаев применение констант-массивов позволяет радикально уменьшить объем кода. Одна из таких ситуаций — использование функции, которая возвращает название месяца. При ее реализации без применения константы-массива функция возврата названия месяца оказывается достаточно длинной.

 

1.function GetMonthName(AMonth:  Integer):  string; 2.end.

 

Синтаксис объявления константы-массива следующий:

 

01.const
02.begin
03.function GetMonthName(AMonth: Integer): string;
04.begin
05.case AMonth of
06.1: Result := 'January';
07.2: Result := 'February';
08.3: Result := 'March';
09.4: Result := 'April';
10.5: Result := 'May';
11.6: Result := 'June';
12.7: Result := 'July';
13.8: Result := 'August';
14.9: Result := 'September';
15.10: Result := 'October';
16.11: Result := 'November';
17.12: Result := 'December';
18.else Result := '';
19.end;
20.end;

 

Значительно более рациональное решение этой задачи — объявить локальную константу-массив. Функция, которая использует константу-массив, значительно короче и выглядит более профессионально.

Листинг 6.4. Использование констант-массивов 

 

01.program Project1;
02.{$APPTYPE CONSOLE}
03.uses
04.SysUtils;
05.function GetMonthName(AMonth: Integer): string;
06.begin
07.case AMonth of
08.1: Result := 'January';
09.2: Result := 'February';
10.3: Result := 'March';
11.4: Result := 'April';
12.5: Result := 'May';
13.6: Result := 'June';
14.7: Result := 'July';
15.8: Result := 'August';
16.9: Result := 'September';
17.10: Result := 'October';
18.11: Result := 'November';
19.12: Result := 'December';
20.else Result := '';
21.end;
22.end;
23.function GetMonthNameEx(AMonthName: Integer): string;
24.const
25.MONTH: array[1..12] of string = ('January', 'February',
26.'March', 'April', 'May', 'June', 'July', 'August',
27.'September', 'October', 'November', 'December');
28.begin
29.if (AMonthName < Low(MONTH)) or (AMonthName > High(MONTH)) then
30.Result := ''
31.else
32.Result := MONTH[AMonthName];
33.end;
34.begin
35.WriteLn(GetMonthNameEx(12));
36.ReadLn;
37.end.
 
Если значение параметра AMonth выходит за рамки допустимого диапазона, функция GetMonthNameEx возвращает пустую строку. Необходимо всегда проверять допустимость вводимых пользователем значений. Если слепо принимать эти значения, даже простые функции, подобные приведенной выше, могут приводить к ошибке и давать неверные результаты. В данном случае, если в качестве параметра AMonth пользователь передает число 13, функция возвращает пустую строку. WriteLn (GetMonthNameEx (13)); Если убрать проверку диапазона, приложение предпримет попытку считывания значения, которое хранится вне массива. Если эта ячейка памяти пуста, вызов, возможно, будет выполнен без проблем, но если это не так, вызов приведет к ошибке. Константы-массивы позволяют также использовать оператор case со строковыми значениями. Хотя строковые значения нельзя непосредственно использовать в операторе case, можно извлечь индекс значения в массиве и затем использовать его в операторе case. Листинг 6.5. Использование строк с оператором case 
01.program Project1;
02.{$APPTYPE CONSOLE}
03.uses
04.SysUtils;
05.function MonthIndex(const AMonthName: string): Integer;
06.const
07.MONTH: array[1..12] of string = ('January', 'February',
08.'March', 'April', 'May', 'June', 'July', 'August',
09.'September', 'October', 'November', 'December');
10.var
11.i: Integer;
12.begin 13.Result := -1;
14.for i := Low(MONTH) to High(MONTH) do
15.begin
16.if AMonthName = MONTH then
17.begin
18.Result := i;
19.Exit;
20.end; // if AMonthName
21.end; // for i
22.end;
23.var
24.userEntry: string;
25.begin
 
������� ������ ��� dle ������� ��������� ������

Помоги проекту! Расскажи друзьям об этом сайте: