Ситкин. Информатика. Программирование в DELPHI
.pdfЛабораторная работа № 8
МАССИВЫ
Цель работы приобретение умений разработки и программной реализации алгоритмов обработки массивов.
Если в проекте осуществляется хранение и обработка большого количества переменных одного типа, то удобно использовать структуру данных массив, а не вводить в употребление большое количество переменных. Достаточно объявить массив. В программировании
массив совокупность пронумерованных однотипных переменных.
Объявление массива
При объявлении массива нужно указать его характеристики:
имя массива общее имя переменных, составляющих массив;
диапазон изменения номера переменной (индекса), тем самым определяется число элементов массива, т.к. значения индекса заполняют без пропусков через единицу весь интервал;
тип элементов массива.
В общем виде объявление (описание) массива выглядит так var имя_массива : array[нижн_индекс..верхн_индекс] of тип ;
здесь array и of служебные слова; имя массива выбирает программист; в квадратных скобках указывают границы изменения индекса, обычно это целые числа; тип тип переменных, составляющих массив. Объявление массива служит командой компилятору для выделения указанного числа пронумерованных ячеек памяти при компиляции для последующего хранения в них значений переменных
91
указанного типа. Сами ячейки пока не содержат значений, они только пронумерованы и имеют общее имя. Т.е. массив не инициализирован.
Реальный массив может быть меньше (но не больше) объявленного.
Рассмотрим примеры объявлений массивов. 1. Через объявление переменной
var x:array[1..10] of byte; //массив из десяти переменных целого типа y:array[0..9] of real; //массив из десяти переменных вещественного
//типа, пронумерованных от 0 до 9 z:array[-5..5] of char; //массив из одиннадцати переменных
//символьного типа, пронумерованных от -5 до 5 d:array[byte] of string; //массив из 256 переменных строкового типа,
//пронумерованных от 0 до 255
2. Через объявление пользовательского типа данных массив, а
затем объявление переменной этого типа type mass = array[1..10] of integer;
var x:mass;
3. С использованием имён констант const m=1; n=25;
type mas1 = array[m..n] of single; mas2 = array[-n..n] of double;
var x:mas1; //массив из 25-ти вещественных переменных (элементов) y, z:mas2; //два массива из 51-го вещественного элемента каждый
В программном коде для обращения к элементу массива запи-
сывают имя массива с указанием номера элемента в квадратных скоб-
ках. В качестве индекса может выступать константа, переменная или выражение. Все они должны быть целого (порядкового) типа, напри-
мер: x[1]; x[i]; x[i+1] и т.п.
92
Обработка массивов
Для обработки массивов используют операторы циклов, в теле
которых индекс пробегает ряд значений. Рассмотрим некоторые ти-
повые примеры (фрагменты). Пусть имеются следующие описания const n=10;
type vector = array[1..n] of real; var x:vector;
S, max:real; i:byte;
1. Сформировать массив из n случайных вещественных чисел в диа-
пазоне от нуля до единицы
Фрагмент блок-схемы |
Реализация с while |
Реализация с for |
|||
|
|
|
|
Randomize; |
Randomize; |
|
i = 1 |
|
|
||
|
|
|
|
i:= 1; |
for i:=1 to n do |
|
|
|
|
||
|
i n |
нет |
while i = n do |
x[i]:=Random; |
|
|
|
|
|
|
|
|
да |
|
|
begin |
|
|
|
|
|
x[i]:=Random; |
|
|
xi=случ. вещ. |
|
|
|
|
|
число 0..1 |
|
|
i:=i+1; |
|
|
|
|
|
|
|
|
|
|
|
end; |
|
|
|
|
|
|
|
|
i = i + 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Чтобы воспользоваться генератором случайных чисел, его нуж-
но сначала инициализировать оператором Randomize, после этого можно вызывать стандартную функцию Random. В работе с массива-
ми удобен оператор цикла for (частный случай оператора while, спе-
циально созданный для работы с массивами).
93
2. Найти сумму элементов массива (элементы уже введены в память)
Фрагмент блок-схемы |
Реализация с while |
Реализация с for |
|||
|
|
|
|
S:=0; |
S:=0; |
|
S = 0, i = 1 |
|
|
||
|
|
|
|
i:= 1; |
for i:=1 to n do |
|
|
|
|
||
|
i n |
нет |
while i = n do |
S:= S + x[i]; |
|
|
|
|
|
|
|
|
да |
|
|
begin |
|
|
|
|
|
S:= S + x[i]; |
|
|
S = S + xi |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i:=i+1; |
|
|
|
|
|
end; |
|
|
|
|
|
|
|
|
i = i + 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Найти номер первого отрицательного элемента (полагаем, что эле-
менты массива введены и хотя бы один из них отрицательный)
Реализация с while |
Реализация с repeat .. until |
i = 1 |
i:= 1; |
i = 0 |
|
|
|
i:= 0; |
|||
|
|
|||
|
while x[i] =0 |
|
repeat |
|
xi 0 |
нет |
|
|
|
do i:=i+1; |
i = i + 1 |
i:=i+1; |
||
да |
Label.Caption:= |
|
until x[i] 0; |
|
|
|
|||
i = i + 1 |
IntToStr(i); |
нет |
Label.Caption:= |
|
xi 0 |
||||
|
|
|||
|
|
да |
IntToStr(i); |
|
вывод i |
|
вывод i |
|
|
|
|
|
94
4. Найти наибольший элемент массива Алгоритм, фрагмент блок-схемы которого представлен на рис. 8.1,
|
|
|
|
следующий. После того, как элементы мас- |
|
max = x1 |
|
|
|||
|
|
сива уже введены в память (в схеме нет вво- |
|||
i = 2 |
|
|
|||
|
|
|
|||
|
|
нет |
да), делают предположение, что первый |
||
|
|
||||
i n |
элемент имеет максимальное значение, ко- |
||||
|
|
||||
да |
|
|
торое записывается в переменную max. За- |
||
xi max |
нет |
тем, начиная со второго элемента, в теле |
|||
|
|
|
|||
да |
|
|
цикла «прогоняют» все остальные элемен- |
||
|
|
|
|||
|
|
|
|
ты, сравнивая с max. Если на i-ой итерации |
|
max = xi |
|
|
|||
|
|
обнаруживается элемент, значение которого |
|||
|
|
|
|
||
|
|
|
|
больше max, то в переменную max записы- |
|
|
|
|
|
||
|
|
|
|
||
i = i + 1 |
|
|
|||
|
|
вается значение i-го элемента, «затирая» |
|||
|
|
|
|
предыдущее значение получают новое |
|
|
|
|
|
||
|
|
|
|
промежуточное значение max. Если в по- |
|
вывод |
|
|
следующей итерации обнаружится элемент |
||
max |
|
|
|||
|
|
|
|||
|
|
|
|
со значением больше нового max, то вновь в |
|
Рис. 8.1 |
|
|
переменную max запишется новое значение |
||
|
|
|
|
и т.д., пока не закончатся элементы. В результате к концу процесса
«вытягивается» максимальное значение массива.
max:= x[1]; i:= 2; |
max:= x[1]; |
while i = n do begin |
for i:=2 to n do |
if x[i] max then |
if x[i] max then |
max:= x[i]; |
max:= x[i]; |
i:=i+1; |
Label.Caption:= FloatToStr(max); |
end; |
|
Label.Caption:= FloatToStr(max); |
|
|
|
95
Пример 8.1 |
|
|
|
|
|
Разработаем алгоритм и проект для умножения отрицательных |
|||||
элементов массива на заданный коэффициент и подсчёта их числа. |
|||||
начало |
|
Блок-схема алгоритма решения пред- |
|||
|
|
|
|
|
|
|
|
ставлена на рис. 8.2. После ввода коэффи- |
|||
ввод |
|
циента |
домножения |
k |
устанавливаются |
коэфф. k |
|
||||
счётчик h=0 |
|
начальные значения |
счётчика отрицатель- |
||
|
|
|
|
|
|
n=число эл-ов |
|
ных элементов h и индекса элемента i. В |
|||
индекс i = 1 |
|
цикле после ввода i-го элемента в память |
|||
|
|
||||
i n |
нет |
каждый проверяется на знак. Если текущий |
|||
|
|||||
да |
|
элемент отрицательный, то он умножается |
|||
ввод xi |
|
на k, это значение «затрёт» старое, а счёт- |
|||
|
|
|
|
|
|
|
|
чик h увеличит своё значение на единицу. В |
|||
xi 0 |
нет |
противном случае эти две операции пропу- |
|||
|
|
|
|
|
|
да |
|
стятся. Независимо от этого условия в теле |
|||
|
|
|
|
|
|
xi = kxi |
|
цикла выводится i-ый элемент и осуществ- |
|||
h = h + 1 |
|
ляется переход к следующему по номеру |
|||
|
|
элементу. Процесс продолжается, пока ин- |
|||
вывод xi |
|
декс не превысит число элементов в масси- |
|||
|
|
ве. Значение счётчика отрицательных чисел |
|||
i = i + 1 |
|
выводится уже после окончания цикличе- |
|||
|
|
ского процесса, т.к. это значение одно. |
|||
|
|
Реализуем алгоритм |
в проекте. Для |
||
вывод h |
|
ввода массива с формы удобно использо- |
|||
|
|
||||
|
|
вать компонент Memo, расположив в нём по |
|||
конец |
|
одному элементу в строке. Свойства ком- |
|||
|
|
||||
Рис. 8.2 |
|
понента описаны в работе 1. |
|||
|
|
96
procedure TForm1.Button1Click(Sender: TObject);
const m=100;
var x: array[1..m] of real;
i, n, h:byte;
k:real;
begin
k:=StrToFloat(Edit1.Text); |
|
h:=0; |
Рис. 8.3 |
|
n:= Memo1.Lines.Count; //определение числа элементов в массиве
for i:=1 to n do
begin //начало тела цикла
{считывание i-го элемента в память из i 1-ой строки компонента Memo1, т.к. нумерация строк в Memo от нуля}
if x[i]<0 then begin //начало действий в случае отрицательного элемента
x[i]:=k*x[i];
h:=h+1;
end; //конец действий для отрицательного элемента
Label3.Caption:=Label3.Caption+#13+FloatToStr(x[i]); {вывод i-го
элемента на отдельной строке с сохранением выведенных ранее элементов} end; //конец тела цикла
Label4.Caption:='отриц. эл-ов '+IntToStr(h);
end;
В случае вывода элементов в компонент Memo2 нужно записать
Memo2.Lines[i-1]:= FloatToStr(x[i]) + #10;
Отметим, что при работе приложения (рис. 8.3) память была от-
ведена при компиляции под сто элементов массива, а реально исполь-
зовалась только под девять, что нерационально.
97
Динамические массивы
При написании программы для обработки массива часто заранее неизвестно реальное будущее количество элементов в нём. Но при объявлении обычного (статического) массива в разделе описаний необходимо указывать число элементов, под это число при компиля-
ции будет выделена память для хранения массива. Исходя из логики,
можно ориентироваться на какое-то предельное число. Однако если реальное число элементов окажется больше, то приложение исполь-
зовать уже нельзя. А если меньше, то программа работать будет, но занято памяти будет меньше, чем выделено, что неэффективно.
Для рационального использования памяти при работе с масси-
вами используют динамические массивы. В отличие от обычных (ста-
тических), при объявлении динамического массива не указывают число элементов. Кроме того, память под такой массив выделяется не во время компиляции, а по ходу работы программы, когда число эле-
ментов становится известно. Для выделения памяти вызывают проце-
дуру SetLength( имя_массива , число элементов ); например,
var x:array of real; //объявляется динамический веществ. массив
n:byte;
begin //раздел операторов
………… //какие-то вычисления до работы с массивом
n:=….. //вычисление количества элементов по ходу работы программы
SetLength(x, n);//выделение памяти под массив сколько и когда нужно
………….//ввод элементов и какие-то действия с массивом
SetLength(x, 0); //высвобождение памяти
…………..//какие-то вычисления, программа ещё работает
end;
98
Под эффективным использованием памяти понимают, что памя-
ти выделяется ровно столько, сколько нужно, и ровно в те моменты работы программы, когда это нужно.
Нижний индекс динамического массива всегда равен нулю,
верхний можно определить функцией High( имя_массива ), её зна-
чение равно числу элементов в массиве минус единица.
Подпрограмма с открытым параметром-массивом
Над массивами часто выполняются типовые для них действия
(поиск максимального элемента, вычисление суммы элементов и т.д.).
В связи с этим целесообразно типовые действия оформлять в виде пользовательских подпрограмм. При описании пользовательской подпрограммы целесообразно в списке формальных параметров у па-
раметра-массива не указывать число элементов, т.е. использовать от-
крытый параметр-массив. Это позволит подпрограмме обрабатывать массивы любой длины, передаваемые ей целиком в качестве фактиче-
ского массива. Должны лишь совпадать типы элементов формального открытого параметра-массива и фактического массива.
Как и обычные параметры подпрограмм, параметры-массивы могут быть параметрами-значениями (по умолчанию они таковые и есть) или параметрами-переменными. Во втором случае перед пара-
метром-массивом должно стоять слово var. Первые передают массив только в подпрограмму, вторые и обратно. Примеры заголовков
procedure ar (t: array[1..5] of real); //обычный массив, параметр-значение procedure ar (t: array of real); //открытый массив, параметр-значение function ar (t: array of real):real; //открытый массив, параметр-значение procedure ar (var t: array of real); //открытый массив, параметр-переменная
99
Пример 8.2
Разработаем проект для ввода двух массивов из компонентов
Memo и вычисления произведения элементов каждого из них.
Заранее неизвестно какое число элементов введёт пользователь в компоненты Memo, это значит, что следует объявить оба массива динамическими и выделить под них память, когда число элементов в каждом массиве станет известно, т.е. уже по ходу работы приложе-
ния. Это позволит эффективнее расходовать память.
Над обоими массивами выполняется одно и то же действие
налицо целесообразность оформить его в виде подпрограммы. Уже
отметили, что число элементов в массивах на стадии разработки про-
екта неизвестно, да и количество элементов в них наверняка будет
разное. По этой причине следует в качестве формального параметра
подпрограммы использовать открытый параметр-массив, причём он |
||||
|
начало |
|
|
будет параметром-значением, т.к. массив нужно |
|
|
|
|
|
|
|
|
|
передавать только в подпрограмму, а возвра- |
|
P = 1, k = 0 |
|
|
щать обратно подпрограмма будет отдельное |
|
|
|
|
число значение произведения элементов пере- |
|
k число |
нет |
||
|
данного ей массива. Блок-схема алгоритма вы- |
|||
|
|
|
||
|
э-ов 1 |
|
|
|
|
да |
|
|
числения произведения, реализованная в нашей |
|
|
|
|
подпрограмме, изображена на рис. 8.4. |
|
P = P·mk |
|
|
|
|
|
|
|
|
|
|
|
|
Расположим на форме две кнопки Button, |
|
|
|
|
связав с ними вычисления произведений для |
|
|
|
|
|
|
k = k + 1 |
|
|
каждого из массивов. Процессы вычислений |
|
|
|
|
|
|
|
|
|
произведений идентичны для обоих массивов, |
|
|
|
|
|
|
конец |
|
|
поэтому приведём одну блок-схему (рис. 8.5). |
|
Рис. 8.4 |
|
|
|
100