Процедуры и функции
В принципе любая программа для каждого конкретного случая может быть написана в виде одного длинного блока текста. Однако такой код будет крайне нерациональным. Например, в программе необходимо несколько раз подсчитать сумму всех членов арифметической прогрессии для разных чисел. Можно, конечно, считать результат каждый раз по известной формуле, но это очень усложнит код. А если неизвестно, для какого количества чисел надо считать эту сумму (например, это определяет пользователь), то написание программы становится почти невозможным. Поэтому в VBA, как и в других современных языках программирования, есть понятие процедур и функций, иначе называемых подпрограммами.
Процедура - это программа, которая может быть вызвана из другой программы и при этом получить для дальнейшей обработки определенную информацию. Например, можно написать процедуру для вычисления суммы всех членов арифметической прогрессии и помещения результата в текст активного документа, и вызывать ее всякий раз, когда надо подсчитать такую сумму, передавая процедуре число, для которого такая сумма считается.
Можно сказать, что программа, не использующая вызов процедур, состоит из одной большой процедуры.
Функция же - это программа, которая не только получает из другой программы какую-нибудь информацию, но и возвращает той программе определенное значение, которое может быть ею в дальнейшем использовано.
В VBA любая процедура, получающая данные из другой программы, имеет следующий формат:
Sub "Название процедуры"("Получаемая переменная" As "Тип данных получаемой переменной")
"Текст процедуры"
End Sub
Получаемых переменных может быть несколько. Для каждой из них желательно указать ее тип, но это можно опустить.
Вызов процедуры происходит так же, как и вызов любой команды VBA, - путем указания ее названия и передаче ей соответствующих значений переменных, указываемых после имени процедуры в команде ее вызова, как, например, в этой программе:
Sub Main()
Dim a As Integer
a = 1
uvelich a, 3
MsgBox a
End Sub
Sub uvelich(b As Integer, c As Integer)
b = b + c
End Sub
Здесь из процедуры Main вызывается процедура uvelich, которой передаются два параметра - a (равный 1) и второй - число 3. Процедура uvelich увеличивает первую переданную переменную на значение второго переданного числа, а затем Main отображает результат.
Стоит помнить, что если в заглавии процедуры указываются типы данных переменных, то и в вызывающей процедуре передаваемые значения должны быть определены и иметь тот же самый тип, иначе VBA выдаст сообщение об ошибке[2].
Переменные могут передаваться в процедуру двумя способами - только для чтения или и для изменения. По умолчанию переменные могут в функции изменяться. Как, скажем, в вышеприведенном примере - переменная "a" была передана в процедуру (под именем "b", чтобы лучше проиллюстрировать этот момент) и там изменилась (к ней прибавили величину переменной "с"), и затем в исходной программе она тоже стала иметь новое измененное значение.
Если же не нужно, чтобы переменная в процедуре менялась (скажем, процедура использует переменную для каких-то своих нужд, не связанных с исходной программой), то перед именем этой переменной в заголовке процедуры следует поместить инструкцию ByVal. Тогда процедура просто использует переменную так, как в этой процедуре описано, возможно, изменив ее значение, а программа, вызвавшая процедуру, дальше будет работать с прежним значением переменной.
К примеру, если бы заголовок процедуры uvelich в вышеприведенном примере имел вид Sub uvelich(ByVal b As Integer, c As Integer), то никакого увеличения переменной "a" не произошло бы и программа отобразила бы в качестве результата число 1. Однако в самой процедуре uvelich соответствующая переменная увеличилась бы на 3 и, если бы последняя ее команда была бы MsgBox b, то она отобразила бы значение 4.
Функция отличается от процедуры тем, что она передает в вызвавшую ее программу определенное значение, которое может быть использовано в дальнейшей работе программы.
В частности, при использовании функции вышеприведенный пример мог бы иметь такой вид:
Sub Main()
Dim a As Integer
Dim d As Integer
a = 1
d = uvelich(a, 3)
MsgBox d
End Sub
Function uvelich(ByVal b As Integer, ByVal c As Integer) As Integer
uvelich = b + c
End Function
Как нетрудно видеть, программа присваивает переменной "d" значение, вычисленное функцией.
При создании функций опять-таки следует помнить, что если в заголовке функции не указана инструкция ByVal перед описаниями переменных, то соответствующие переменные могут быть в функции изменены и после ее выполнения в исходной программе они будут иметь уже измененные значения. Так, если бы данный пример имел вид
Sub Main()
Dim a As Integer
Dim d As Integer
a = 1
d = uvelich(a, 3)
MsgBox d
MsgBox a
End Sub
Function uvelich(b As Integer, c As Integer) As Integer
uvelich = b + c
b=1555
End Function,
то программа бы в качестве значения переменной "а" отобразила бы число 1555, а не 1.
Указания типов переменных в заголовках функций и процедур необязательны - их можно опустить. В этом случае будет считаться, что все передаваемые переменные могут иметь любой тип (точнее, они просто преобразуются в тип Variant и такими уже используются в подпрограмме). Однако необходимо строго следить за соответствием типов переменных в заголовках подпрограмм и в вызывающей их программе. Так, если в заголовке функции указано, что первая переменная, передаваемая ей, имеет тип Integer, то та переменная, которая передается в функцию как первая (в последнем примере - "a"), должна быть определена именно как Integer до вызова функции (что мы и видим в этом примере).
Перед заголовком функции или процедуры можно поставить инструкции Public или Private ("Private Function uvelich(b As Integer, c As Integer) As Integer"). Функция или процедура, объявленная как Public, может вызываться и из других модулей, в то время как функция или процедура, объявленная как Private, доступна только из данного модуля.
По умолчанию все функции и процедуры считаются объявленными как Public (а переменные - объявленные как Private!).
Если в программе есть вложенные процедуры или функции (то есть процедура или функция вызывает другую процедуру или функцию, которая, в свою очередь, вызывает еще одну процедуру или функцию и.т.д.), то их взаимоотношения ("кто кого вызывает?") удобно при отладке отслеживать с помощью окна Стек Вызова (рис.2.3), в котором видны все произошедшие вызовы.
В выпадающем меню в правом верхнем углу окна программы (см. на рис.2.2) перечислены все процедуры и функции открытого модуля. Это меню можно использовать для быстрого перехода к необходимому месту модуля, а также в раздел описаний переменных, указание на который стоит в этом меню первым.
Рис.2.3. Окно Стек вызова.
Главная процедура - собственно программа - не может иметь параметров (в ее заголовке должны стоять пустые скобки). Только таким процедурам можно назначать кнопки и сочетания клавиш для их вызова на выполнение.
Во время пошагового исполнения при отладке программы с процедурами или функциями имеется возможность отказаться от прохода по всем командам той или иной процедуры или функции, вызвав команду "Шаг с обходом" из меню Отладка или кнопкой на одноименной панели. Тогда вся процедура или функция будет выполнена безостановочно, как при обычном исполнении.
Команда "Шаг с выходом" доступна в любой процедуре или функции, включая главную, и выполняет в обычном режиме все команды до конца процедуры или функции, переходя затем в режим пошагового исполнения в вызвавшей программе.
Разбивая программу на отдельные процедуры и функции, вы получаете возможность легко использовать повторно одни и те же фрагменты кода, обращаясь к ним из программы по мере надобности. К сожалению, увеличить быстродействие программы на VBA при этом не удастся, так как при запуске в шаблоне хотя бы одного макроса он компилируется полностью (в то время как в программах, написанных на многих других языках программирования, загрузка в память функций и процедур происходит лишь по мере обращения к ним).