Массивы очень упрощают процесс программирования. Без них практически невозможно написать универсальную программу. Например, представьте себе, что вы манипулируете информацией о квартирах жилого дома. Вы объявляете переменные K1 - для первой квартиры, K2 - для второй и так далее. K1=54 будет означать, что площадь первой квартиры 54 кв.м., К2=72 и т.д. Теперь представим, что нужно подсчитать общую площадь всех квартир в доме. Очевидно, что это $S = K1+K2+...+K_n$. В одном доме у нас 36 квартир, а в другом 144. Если в первом случае нужно будет использовать 36 отдельных переменных для вычисления общей площади, то для второго дома уже 144. Очень быстро приходим к мысли, что нам необходима переменная, состоящая из нумерованных ячеек.
Массив - переменная, состоящая из некоторого количества однотипных элементов. У массива, как и у любой другой переменной, есть имя. А доступ к конкретному элементу массива осуществляется через указание в скобках после имени его индекса.
Например, A(5) означает, что обращаемся к элементу с индексом 5 массива, имеющего имя A.
Массивы в VBA и во многих других языках программирования делятся на 2 класса:
- Фиксированные массивы. Такие массивы состоят из заранее известного количества элементов. Это количество определяется во время объявления массива и уже не может быть изменено в процессе его жизненного цикла. Вы, конечно же, сможете использовать меньшее количество элементов, но не существует способа увеличить количество элементов сверх объявленного.
- Динамические массивы. Эти массивы можно "переобъявлять" в процессе жизненного цикла. То есть мы можем управлять количеством элементов динамических массивов в зависимости от наших потребностей. Это очень удобно, так как в подавляющем большинстве случаев программист не может заранее знать, с каким объёмом данных он столкнётся.
Объявление фиксированных массивов
Фиксированные массивы объявляться следующим образом:
- Dim arrTemp(10) as Long
- Dim arrTest(32) as String
Рекомендуется при объявлении массивов VBA давать всем именам префикс "arr".
Как мы видим, в примере объявлено 2 одномерных массива arrTemp и arrTest. Одномерные массивы в программировании также часто называют векторами. Типом элементов первого массива является Long, второго массива - String. В этом типе синтаксиса в скобках указан максимальный индекс (верхняя граница) элемента массива. А что насчёт минимального индекса (нижней границы) массива? По-умолчанию минимальным индексом является ноль. В данном случае стандартное поведение интерпретатора языка VBA можно изменить при помощи оператора option base {0|1}. Option base 1 заставляет VBA считать нижней границей массива - единицу, а не ноль.
Таким образом, по умолчанию массив arrTemp имеет 11 элементов - от 0 до 10. Но, если в начало модуля, в котором этот массив объявляется, вставить оператор Option Base 1, то массив arrTemp будет иметь 10 элементов - от 1 до 10.
Помимо вышеуказанного вы вправе использовать следующий синтаксис, который НЕ зависит от option base {0|1}:
- Dim arrFlat(0 To 4) as Boolean
- Dim arrData(5 To 25) as Integer
- Dim arrCazus(-5 To -1) as Single
Таким образом, вы в явном виде указываете и нижнюю, и верхнюю границы. Как видите, нижняя граница совершенно не обязательно должна начинаться с 0 или 1. Более того, индексы границ могут принимать и отрицательные значения, главное, чтобы нижняя была меньше верхней.
Помимо одномерных массивов, можно объявлять и массивы с размерностью больше единицы.
- Dim arrMulti(10,30) as Range
- Dim arrData3(0 To 1, 1 To 3, 1 To 10) as Variant
В приведенном выше примере arrMulti - двумерный массив, а arrData3 - трёхмерный. Первый содержит $11\cdot 31=341$ элемент, второй - $2\cdot 3\cdot 10=60$ элементов. Теоретически допускается объявлять до 60 размерностей массива.
Какие типы данных могут стать элементами массива? Абсолютно любой тип данных годится на роль элемента массива, включая объектные типы User Data Type, другие массивы (через тип Variant). Если вы не указываете при объявлении тип данных массива, то предполагается, что этим типом является тип Variant.
Объявление динамических массивов
Динамические массивы объявляться следующим образом:
- ' Обратите внимание, что границы не указываются
- Dim arrOpen1() as Single
- Dim arrOpen2()
Однако, использовать их после такого объявления пока ещё нельзя. Необходимо выделить память под массив. Особенность работы с динамическим массивом как раз состоит в том, что программист отвечает за его своевременное расширение (усечение) в памяти. Для этого существует специальный оператор, который имеет следующий синтаксис:
ReDim [Preserve] varname(subscripts) [As Type]
Например:
1 ReDim arrOpen(5)
После этого оператора, вы можете использовать элементы массива arrOpen с 0-го по 5-й. Всё, что мы говорили про оператор option base и нижнюю границу, верно и для динамических массивов. Предположим, что вы сохранили информацию в элементах 0-5 и у вас готова новая порция информации для элементов 6-11. Чтобы разместить в данном массиве новые элементы и не потерять старые, вы должны сделать следующее:
1 ReDim Preserve arrOpen(11)
То есть увеличим верхнюю границу массива и используем ключевое слово Preserve, чтобы во время этой операции не потерять текущее содержимое arrOpen, так как в противном случае (без слова Preserve) массив будет расширен, а память заполнена нулями. Вы также вправе вообще не декларировать массив оператором Dim, а сделать это впервые через ReDim и там же указать тип элементов. Но, если вы в первом ReDim (или Dim) указали определенный тип элементов, то в последующих операторах ReDim этот тип переопределён быть не может - возникнет ошибка на этапе компиляции проекта.
Как и с обычными переменными запись информации в элемент массива происходит через оператор присваивания (=), но указанием индекса элемента массива.
Определение границ массива
В подпрограммах часто приходится иметь дело с массивами, которые переданы вам в качестве параметра (как это сделать показано ниже), поэтому в этом случае очень актуален вопрос определения нижней и верхней границ индекса массива. Для этого в языке предусмотрены 2 функции: LBound и UBound. Первая возвращает нижнюю границу индекса, вторая - верхнюю.
LBound ( array [, dimension ] )
UBound ( array [, dimension ] )
Для одномерных массивов параметр dimension можно не указывать. Для многомерных массивов его указывать необходимо. Это означает, что, если вы точно не знаете, с каким массивом имеете дело, но необходимо узнать его первую размерность, то лучше использовать вариант UBound(arrTemp,1), а не UBound(arrTemp), так как последний вариант вызовет ошибку, если массив окажется многомерным.
Если вы ошибетесь с указанием правильного индекса массива, то возникнет ошибка периода исполнения с кодом 9. Эта же ошибка возникнет, если вы в функции LBound / UBound укажете несуществующую размерность массива (например, 3 для двумерного массива).
Перебор элементов массива
Собственно, массивы нужны для того, чтобы хранить в них однотипную информацию и перебирать их в цикле. Как правило, алгоритм делает что-то полезное с одним элементом массива, а цикл повторяет эти типовые действия для всех элементов массива.
Наиболее удобный оператор цикла для перебора элементов массива - это безусловно For ...Next.
- Dim arrTemp(100) as Double
- Dim i as Long
- ' Обратите внимание, как мы определяем границы цикла
- For i = LBound(arrTemp) to UBound(arrTemp)
- ' Вычисляем квадратный корень для каждого элемента
- arrTemp(i) = Sqr(arrTemp(i))
- Next
- ' Если вы хотите перебирать элементы от верхней границы к нижней, то
- For i = UBound(arrTemp) to LBound(arrTemp) Step -1
- arrTemp(i) = Sqr(arrTemp(i))
- Next
Можно, конечно, перебирать массив и в других типах циклов Do ... Loop, но смысла и удобства в этом не много.
Функция ARRAY
Данная функция полезна для создания справочных массивов. Она возвращает переменную типа Variant, содержащую одномерный массив с типом элементов Variant.
Array ( arglist )
- Dim arrTest
- arrTest = Array(30,40,50,60)
- MsgBox arrTest(2) ' вернёт 50
- 'Согласитесь, что это удобнее, чем:
- Dim arrTest2 as Integer
- arrTest2(0)=30 : arrTest2(1)=40 : arrTest2(2)=50 : arrTest2(3)=60
Вызов функции без параметров приведёт к возврату массива нулевой длины. При этом будет наблюдаться интересный эффект: LBound вернёт вам 0, а UBound вернёт -1, то есть верхняя граница окажется меньше нижней границы.
Функция SPLIT
Split возвращает одномерный массив, содержащий подстроки, из строкового выражении с учётом указанного разделителя
Split(expression[, delimiter[, limit[, compare]]]), где:
- expression - строковое выражение, содержащее подстроки и разделители, обязательный параметр;
- delimiter - текстовый разделитель, необязательный параметр, если опущен, то предполагается, что разделителем является символ пробела;
- limit - количество подстрок, которое необходимо вернуть. -1 или отсутствие параметра означает, что вернуть надо все подстроки;
- compare - константа, указывающая тип сравнения для символов разделителей. 1 - текстовое сравнение (без учёта регистра), 0 - бинарное сравнение (с учётом регистра).
- Dim s As String
- Dim arrTemp
- s = "A;B;C;D;1;2;3;4"
- arrTemp = Split(s, ";", 5)
Если вы в качестве разделителя укажете пустую строку, то на выходе получите массив, состоящий из одного элемента. Кстати, split всегда возвращает массив с нулевой нижней границей вне всякой зависимости от наличия option base 1.