Изучение простых, многомерных и динамических массивов.
В практике программирования нередко возникает необходимость обработать большое количество однотипных данных. Допустим, распределить недельную прибыль кафе по дням. Что для этого нужно? Использовать переменные типа currency, как раз для таких случаев и применяемые. И сколько переменных нам нужно? Ну, ясен вопрос - по переменной на день, всего семь штук. Давайте посмотрим, как такую задачу можно было бы решить на Lazarus (пример академический, выполнять его не нужно):
var
day1, day2, day3, day4, day5, day6, day7: currency;
Довольно длинное объявление получилось, правда? А если бы нужно было посчитать не недельную прибыль, а месячную? Или годовую? Теперь предположим, что нам нужно посчитать сумму этой прибыли. Это выглядело бы примерно так:
pribil:= day1 + day2 + day3 + day4 + day5 + day6 + day7;
А если бы пришлось считать месячную или годовую прибыль? Какой длины оператор тогда бы получился? А ведь часто бывают и более сложные расчеты, которые требуется провести с каждой однотипной переменной.
К счастью, в языках программирования существует такой инструмент, как массивы.
Фактически, получается цепочка однотипных переменных под общим именем, где у каждой такой переменной имеется собственный номер в ячейке - индекс. Такие переменные называются элементами массива, каждый элемент имеет свой номер. Объявляется массив в разделе переменных следующим образом:
var
a: array[1..7] of currency;
Ключевое слово array указывает, что это будет массив. Числа в квадратных скобках разделяются двумя точками и обозначают диапазон индексов. Так, [1..7] означает, что индексы массива будут от 1 до 7, то есть, 7 штук. После ключевого слова of указывается тип массива, такой же тип будет и у каждого элемента массива. В результате, компилятор создает в оперативной памяти семь ячеек по 8 байт каждая (тип currency занимает 8 байт):
(рис 13.1) Размещение элементов массива a в памяти
Тип данных, используемый в массивах, так же может быть любым. Во многих других языках программирования диапазон индексов всегда начинается с 0. В языках, основанных на Паскале, диапазон может начинаться с любой цифры: с 0, 1, или 438, если вам так нравится. Хотя программисты обычно начинают диапазон либо с 0, либо с 1, в зависимости от обстоятельств. Пример объявления различных массивов:
var
name: array[0..30] of string;
year: array[1..2013] of integer;
man: array[10..25] of boolean'
Хорошо, массивы мы объявили. Что с ними делать дальше? Мы можем обратиться к любому элементу массива, указав после имени в квадратных скобках индекс этого элемента. Так, мы можем присвоить значение элементу или получить его значение. Другими словами, мы можем обращаться с элементами массива, как с отдельными переменными. Примеры:
…
a[1]:= 3520.45;
a[2]:= a[1] + 12.4;
ShowMessage('Прибыль за пятницу составила ' + FloatToStr(a[5]));
…
Ну, ладно, мы научились создавать и использовать массивы. Но какие же преимущества у массивов в отличие от простых однотипных переменных? О первом таком преимуществе мы уже говорили, вот как просто можно объявить массив с элементами, рассчитанными на год:
var
a: array[1..365] of currency;
Попробуйте-ка объявить 365 отдельных переменных! Второе преимущество гораздо важней: обрабатывать элементы массива намного проще, чем отдельные переменные. Например, нам нужно создать программу, которая бы могла сделать перевод температур из привычных нам Цельсиев в непривычные американские Фаренгейты. И пусть диапазон возможных температур будет от -100 градусов до +200. Идея такова: пользователь вводит нужную температуру, а программа выводит результат на экран. Конечно, проще было бы делать нужный расчет в момент, когда пользователь введет свои данные, и нажмет кнопку "рассчитать". Но мы поступим по-другому: мы сначала сделаем все расчеты и поместим результаты в массив, а затем будем просто выводить нужные данные. Массив сделаем глобальным, чтобы заполнять его в одном событии, а выводить данные в другом. Открываем Lazarus с новым проектом, сохраняем его сразу в папку 13-01 там, где у нас хранятся все учебные проекты. Проект сохраним под именем MyTemp, а модуль главной формы, как обычно - Main. Займемся главной формой, измените у нее следующие параметры:
Name = fMainCaption = Перевод температурных шкалBorderStyle = bsDialogPosition = poMainFormCenterДалее, перейдите на вкладку Additional Палитры компонентов, найдите и установите на форму компонент TStaticText, который предназначен для вывода на экран пояснительного текста, в том числе и многострочного. Если вы забыли, то найти нужный компонент просто: нужно просто подвести указатель мыши к компоненту, и через короткое время появится всплывающая подсказка с именем компонента.
В принципе, для этих целей можно использовать и простую метку TLabel, но было бы интересней познакомиться и с другими компонентами. В Инспекторе объектов в свойстве Caption нажмите на кнопку "…" справа от свойства:
(рис 13.2) Свойство Caption компонента TStaticText
В открывшемся редакторе напишите следующий текст:
Укажите температуру в Цельсиях
от -100 до 200 градусов:
Именно так, в две строки. Затем измените размеры компонента на форме, чтобы текст отображался точно в таком виде, как мы ввели. И еще: в свойстве Alignment выберите taRightJustify, чтобы текст выравнивался по правому краю.
Теперь перейдите на вкладку Misc палитры компонентов, найдите там и установите на форму, правее TStaticText, компонент TSpinEdit, который предназначен для ввода пользователем целых чисел. Этот компонент похож на простой TEdit, но справа от него есть стрелки вверх и вниз, которыми пользователь может прибавлять или убавлять указанное в строке число. Использование этого компонента гарантирует нам, что пользователь не сможет ввести в него ничего, кроме целого числа. А чтобы еще гарантировать правильный диапазон этих чисел, в свойстве MaxValue (максимальное возможное значение) установите 200, а в MinValue (минимальное возможное значение) установите -100 (минус сто). Теперь пользователь не сможет ввести в это поле ничего, кроме целых чисел. Если же он попытается превысить максимальное значение, то компонент автоматически установит число 200 - максимально возможное.
И наоборот, если указать число меньше, чем -100, компонент установит -100, так что нам не нужно беспокоиться об ошибках. Кроме того, в этом компоненте позже нам понадобятся ещё два свойства: Text (установленное значение в виде строки), и Value (установленное значение в виде целого числа).
По умолчанию, компонент недостаточно широк, поэтому установите в его свойстве Width значение 75. И еще: нам придется обращаться к компоненту по имени, а SpinEdit1 - это слишком длинное имя. Измените его свойство Name на SE1.
Ну и наконец, установите на форму простую кнопку, в свойстве Caption которой напишите Рассчитать температуру. Надпись на кнопке не уместится, поэтому кнопку придется удлинить. Расположите компоненты на форме примерно так:
(рис 13.3) Внешний вид формы
Теперь займемся предварительными расчетами. Для начала нам нужно сделать глобальный массив вещественных чисел - температура по Фаренгейту уже не будет целым числом. Значение по Фаренгейту вычисляются так: берется температура по Цельсию, умножается на 9/5, затем к результату прибавляется 32. Поэтому в глобальном разделе var, сразу над ключевым словом implementation объявляем массив atemp:
var
fMain: TfMain;
atemp: array[-100..200] of real; //наш глобальный массив
implementation
Поскольку это глобальный массив, он будет создан сразу же при загрузке программы, и будет существовать, пока программа не завершит свою работу. Теперь займемся заполнением этого массива.
Выделите форму, щелкнув по её свободному месту, затем в Инспекторе объектов перейдите на вкладку События. Нам нужно событие OnCreate - оно возникает только однажды, при создании формы. Щелкните дважды по этому событию, чтобы сгенерировать его. Код события следующий:
procedure TfMain.FormCreate(Sender: TObject);
var
i: smallint; //счетчик для цикла
begin
for i:= -100 to 200 do
atemp[i]:= i * 9/5 + 32;
end;
Вот теперь, наконец, мы сможем оценить всю прелесть использования массивов! Давайте разберем этот код. В разделе var мы объявили переменную i типа smallint. Это - счетчик для цикла for, который мы использовали в коде. Так как диапазон нужных нам значений располагается от -100 до +200, тип целых чисел smallint - самый подходящий. А теперь обратите внимание на код:
for i:= -100 to 200 do
atemp[i]:= i * 9/5 + 32;
Это - самая главная "фишка" массивов - обработка всего массива, каким бы большим он не был, в простом цикле. Вначале счетчик i принимает значение -100. Внутри цикла мы присваиваем значение одному элементу массива. Если мы заменим переменную i на её значение, как это делает компилятор в процессе работы, то получим следующее:
atemp[-100]:= -100 * 9/5 + 32;
Таким образом, мы высчитываем перевод температуры из Цельсия в Фаренгейты для конкретной температуры: -100 градусов по Цельсию, и полученный результат присваиваем элементу массива atemp[-100]. Затем цикл повторяется, i увеличивается на единицу, и уже равна -99. Новое значение присваивается новому элементу массива, и так до конца цикла. Как вы думаете, сколько элементов массива будет заполнено в этом цикле? Кто сказал 300?! А про ноль вы забыли? 301 раз будет работать цикл, и 301 различное значение будет присвоено 301 элементу массива. А теперь представьте, как это выглядело бы для отдельных переменных:
t_100:= -100 * 9/5 + 32;
t_99:= -99 * 9/5 + 32;
…
t200:= 200 * 9/5 + 32;
Вместо двух строчек цикла мы получили бы 301 строку кода с одинаковыми вычислениями!
Но вернемся к нашему проекту, ведь нам еще нужно вывести на экран результат перевода температур. Сгенерируем для кнопки Button1 событие OnClick. Её код будет совсем простой:
procedure TfMain.Button1Click(Sender: TObject);
begin
ShowMessage(SE1.Text + ' Цельсия = ' +
FloatToStr(atemp[SE1.Value]) + ' Фаренгейта');
end;
Здесь у нас всего один оператор - процедура ShowMessage(). Она формирует выводимое сообщение из 4 частей: SE1.Text - это то значение, которое установил пользователь в компоненте TSpinEdit в виде строки. Затем мы вставляем в сообщение текст ' Цельсия = '. Обратите внимание на пробелы вначале и в конце этого кусочка - без них слова слипались бы, а это некрасиво. Далее, мы добавляем к строке следующий код:
FloatToStr(atemp[SE1.Value])
Здесь SE1.Value - это то значение, которое установил пользователь, но уже в виде целого числа! Допустим, пользователь установил значение 35, тогда этот код будет таким:
FloatToStr(atemp[35])
В элементе массива atemp[35] у нас уже хранится переведенное в Фаренгейты значение температуры 35 по Цельсию. А функция FloatToStr() только переведет это значение в строку. Таким образом, для значения 35 сформируется сообщение: "35 Цельсия = 95 Фаренгейта". Сохраните проект, скомпилируйте его и убедитесь, что программа корректно работает в указанных пределах температур, а превысить их пользователь не сможет, если и захочет, как не сможет ввести ничего другого, кроме целого числа.
Массивы, которые мы до сих пор разбирали, были одномерными - визуально их можно представить, как цепочку ячеек или одну строку таблицы. Однако бывают ситуации, когда этого недостаточно. Допустим, нужно сохранить какую-то таблицу. Одномерный массив с такой задачей не справится. Для этого существуют многомерные массивы, которые имеют размерность от двух и больше. Например, объявить двухмерные массивы из 5 строк и 10 колонок можно следующим образом:
var
a: array[1..5, 1..10] of string;
b: array[0..4, 0..9] of integer;
То есть, в квадратных скобках через запятую указывается диапазон вначале строк, а затем колонок. Визуально, такой массив можно было бы представить так:
| 1,1 | 1,2 | 1,3 | 1,4 | 1,5 | 1,6 | 1,7 | 1,8 | 1,9 | 1,10 |
| 2,1 | 2,2 | 2,3 | 2,4 | 2,5 | 2,6 | 2,7 | 2,8 | 2,9 | 2,10 |
| 3,1 | 3,2 | 3,3 | 3,4 | 3,5 | 3,6 | 3,7 | 3,8 | 3,9 | 3,10 |
| 4,1 | 4,2 | 4,3 | 4,4 | 4,5 | 4,6 | 4,7 | 4,8 | 4,9 | 4,10 |
| 5,1 | 5,2 | 5,3 | 5,4 | 5,5 | 5,6 | 5,7 | 5,8 | 5,9 | 5,10 |
Обратиться к отдельному элементу массива можно двумя способами:
a[2, 9]:= 'Строка 1'; //1-й способ
a[3][10]:= 'Строка 2'; //2-й способ
В первом случае мы обращаемся к элементу во второй строке, девятой колонке. Во втором - к третьей строке, десятой колонке. Первый способ обращения к элементу кажется компактней, его мы и будем использовать впредь.
Доработаем наш проект. Что, если мы будем конвертировать нашу температуру не только в Фаренгейты, но и в Кельвины (единица термодинамической температуры в СИ - Международной Системе Единиц)? Пересчет Цельсия в Кельвины еще проще, чем в Фаренгейты: нужно к температуре по Цельсию прибавить 273,15. Для этого нам потребуется вместо одномерного массива объявить двухмерный. Измените код над служебным словом implementation:
atemp: array[1..2, -100..200] of real;
В первой строке у нас будут Фаренгейты, во второй - Кельвины. А колонки мы отведем под требуемый диапазон. Далее, нам потребуется изменить цикл в событии OnCreate формы:
procedure TfMain.FormCreate(Sender: TObject);
var
i: smallint;
begin
for i:= -100 to 200 do begin
atemp[1, i]:= i * 9/5 + 32; //Фаренгейты
atemp[2, i]:= i + 273.15; //Кельвины
end;
end;
Обратите внимание: поскольку нам в цикле требуется выполнить не один оператор, а два, нам пришлось заключить их в программные скобки begin…end, сделав составной оператор. Далее, все просто: для строки 1 нашего массива мы рассчитываем Фаренгейты, как в прошлый раз. А для строки 2 уже Кельвины.
Но нам потребуется переделать и событие кнопки OnClick:
procedure TfMain.Button1Click(Sender: TObject);
begin
ShowMessage(SE1.Text + ' Цельсия будет равно:' + #13 +
FloatToStr(atemp[1, SE1.Value]) + ' Фаренгейта' + #13 +
FloatToStr(atemp[2, SE1.Value]) + ' Кельвина');
end;
С кодом, надеюсь, вы разберетесь самостоятельно? Итак, если пользователь введет, например, 36 градусов Цельсия, то нажав на кнопку, получит следующее сообщение:
(рис 13.4) Сгенерированное программой сообщение
Но размерность массива может быть и больше. Трехмерный массив визуально можно представить следующим образом:
(рис 13.5) Визуальное представление трехмерного массива
Объявить подобный массив можно было бы так:
var
a: array[1..5, 1..5, 1..5] of integer;
А обращаться к отдельным элементам так:
a[2, 3, 4]:= 12;
Как бы выглядел четырехмерный массив визуально вообще невозможно представить. У вас есть возможность работать хоть с десятимерным массивом, однако в практике программирования обычно используют одно- и двухмерные массивы, и очень редко возникает надобность в трехмерном. Не забывайте про один из основных принципов программирования, который сами программисты называют KISS (Keep It Simple, Stupid - будь проще, дурачок). Этот принцип подразумевает, что код программы не стоит усложнять без нужды - сложный код тяжелее воспринимается самим программистом, больше нагружает процессор, а ошибки времени выполнения (run time errors - ошибки, возникающие во время выполнения программы, обычно это логические ошибки) в таком коде сложнее отслеживать.
В практике программирования иной раз случается работать с массивами, размерность которых заранее неизвестна - она может зависеть от данных, введенных пользователем, от текущих параметров, от объема обрабатываемой информации, да еще много от чего. Программист в этих случаях не может указать конкретные границы индексов массива. В таких случаях используют динамические массивы.
Объявляются динамические массивы также в разделе var, следующим образом:
var
da: array of integer;
Как видите, мы указываем только тип массива, но не его размерность. Память под объявленный массив при этом не отводится. В дальнейшем с динамическим массивом мы можем производить различные операции: устанавливать размерность (длину) массива, увеличивать или уменьшать эту размерность (потому массив и динамический), узнавать высшие и низшие границы диапазона индексов массива, присваивать значения отдельным элементам или наоборот, считывать эти значения. Познакомьтесь с функциями, которые все это проделывают.
SetLength() - устанавливает размер массива. Синтаксис:
SetLength(<массив>, <длина>);
Пример:
SetLength(da, 5); //установили размер массива в 5 элементов
Тут следует оговориться, что в отличие от обычного массива, начальный индекс которого может быть любым, индексация динамического массива всегда начинается с нуля. То есть, индексация элементов массива в нашем примере будет от 0 до 4 - всего пять элементов. Как только мы установили размер массива, в памяти выделяется место под него. В нашем примере будет отведено по 4 байта (тип integer) для 5 элементов массива. Обращаться к этим элементам можно обычным образом:
da[0]:= 5;
da[4]:= da[0];
Теперь мы можем прибавить элемент к массиву, установив для него новый размер:
SetLength(da, 6);
В этом случае, один пустой элемент будет добавлен в конец массива. Если же напротив, мы уменьшим его размер:
SetLength(da, 4);
то массив усекается. Если последний элемент содержал какие-то данные, они будут потеряны. Освободить память массива можно, присвоив ему нулевую размерность:
SetLength(da, 0);
Также освободить память можно, присвоив массиву значение nil (ничего, пусто):
da:= nil;
Впрочем, этого можно и не делать: по окончании процедуры, где данный массив был объявлен, память будет освобождена автоматически.
Динамические массивы могут быть и многомерными. Например, двухмерный массив целых чисел можно объявить так:
var
a: array of array of integer;
Затем такому массиву можно присвоить размерность, например, 4 на 5:
SetLength(a, 4, 5);
Это будет, как если бы мы объявили простой массив:
var
a2: array[0..3, 0..4];
Length() - возвращает размер динамического массива, то есть, количество его элементов. Например, нам нужно посмотреть размер массива, и если он пустой, то добавить в него один элемент:
if Length(da) = 0 then SetLength(da, 1);
Low() - возвращает нижний индекс массива, у динамических массивов это всегда ноль.
High() - возвращает верхний индекс массива, но это не то же самое, что количество элементов. Если у массива 5 элементов, то Length() вернет 5, а High() вернет 4, так как индексация начинается с нуля.
Пример обхода массива от первого до последнего элемента:
for i:= Low(da) to High(da) do
da[i]:= i * i;
В примере мы каждому элементу присваиваем квадрат его индекса. Давайте поработаем с динамическими массивами на практике. Чтобы не портить наш конвертер температур, закройте его и откройте новый проект. Мудрить мы не будем - нам нужна только форма и простая кнопка на ней. Переименовывать тоже ничего не будем, это же просто пример. Сохраните новый проект в папку 13-02.
Сгенерируйте процедуру OnClick для кнопки, её код будет таким:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string; //для запроса
da: array of string; //динамический массив строк
i: integer; //счетчик
begin
//узнаем у пользователя, сколько строк делать:
s:= '0';
InputQuery('Привет!', 'Сколько строк желаете создать?', s);
//если ноль, то ничего не делаем, выходим из процедуры:
if StrToInt(s) = 0 then Exit;
//иначе устанавливаем указанную размерность массива:
SetLength(da, StrToInt(s));
//теперь обойдем весь массив, устанавливая в его элементы значения:
for i:= Low(da) to High(da) do
da[i]:= 'Строка №' + IntToStr(i + 1);
//теперь снова обойдем массив и создадим сообщение из его строк:
s:= '';
for i:= Low(da) to High(da) do
s:= s + da[i] + #13;
ShowMessage(s);
end;
Код содержит достаточно подробные комментарии. Вначале в строковую переменную s мы получаем количество желаемых строк. Так как функция InputQuery() возвращает только строку, нам придется получать целое число в виде строки - никакой проверки мы здесь не делаем, это же только демонстрация работы с динамическим массивом. Поэтому правильность вводимого числа оставим на совести пользователя.
Далее, мы проверяем - не ноль ли это? Если строк создавать не нужно, мы просто выходим из процедуры, пропуская весь остальной код. Если же пользователь ввел какую-то цифру, причем она должна быть больше нуля, то мы устанавливаем указанную размерность массива:
SetLength(da, StrToInt(s));
Далее, с помощью цикла for мы обходим весь массив от первого до последнего элемента. В каждый элемент мы записываем строку: "Строка №", добавляем номер элемента и переход на новую строку. Обратите внимание, для первой строки i будет равно 0, поэтому мы указываем i + 1:
for i:= Low(da) to High(da) do
da[i]:= 'Строка №' + IntToStr(i + 1);
Затем мы очищаем строковую переменную s, чтобы вторично воспользоваться ей, собрать в неё сообщение. Что и делаем в следующем цикле:
for i:= Low(da) to High(da) do
s:= s + da[i] + #13;
Получается, в процедуре мы дважды обходили массив: сначала чтобы занести в него значения, затем чтобы считать их. Теперь сохраните проект, скомпилируйте его и запустите на выполнение. Имейте в виду, если вы укажете слишком большое число, то окно сообщения не уместится в экране - в этом случае закрыть его можно будет стандартными клавишами <Alt + F4>.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.