Циклы


В VBA имеется богатый выбор средств организации циклов, которые можно разделить на две основные группы — циклы с условием Do… Loop и циклы с перечислением For…Next.
Циклы типа Do … Loop используются в тех случаях, когда заранее неизвестно, сколько раз должно быть повторено выполнение блока операторов, составляющего тело цикла. Такой цикл продолжает свою работу до тех пор, пока не будет выполнено определенное условие. Существуют четыре вида циклов Do…Loop, которые различаются типом проверяемого условия и временем выполнения этой проверки. В приводится синтаксис этих четырех конструкций.
Синтаксис операторов цикла Do

Конструкция

Описание

Do While <условие> <блокОператоров> Loop

Условие проверяется до того, как выполняется группа операторов, образующих тело цикла. Цикл продолжает свою работу, пока это условие выполняется (то есть имеет значение True), иными словами, в этой конструкции указывается условие продолжения работы цикла

Do Until <условие> <блокОператоров> Loop

Условие проверяется до того, как выполняется группа операторов, образующих тело цикла. Цикл продолжает свою работу, если это условие еще не выполнено, и прекращает работу, когда оно станет истинным, иными словами, в этой конструкции указывается условие прекращения работы цикла

Do <блокОператоров> Loop Until <условие>

Условие проверяется после того, как операторы, составляющие тело цикла, будут выполнены хотя бы один раз. Цикл продолжает свою работу, если это условие еще не выполнено, а когда оно станет истинным, цикл прекращает работу, иными словами, в этой конструкции указывается условие прекращения работы цикла

Do
<блокОператоров> Loop While <условие>

Условие проверяется после того, как операторы, составляющие тело цикла, будут выполнены хотя бы один раз. Цикл продолжает свою работу, пока это условие остается истинным, иными словами, в этой конструкции указывается условие продолжения работы цикла

 
Имеется также две разновидности оператора цикла с перечислением For. . .Next. Очень часто при обработке массивов, а также в тех случаях, когда требуется повторить выполнение некоторой группы операторов заданное число раз, используется цикл For. . .Next со счетчиком. В отличие от циклов Do. . .Loop, данный тип цикла использует специальную переменную, называемую счетчиком, значение которой увеличивается или уменьшается при каждом выполнении тела цикла на определенную величину. Когда значение этой переменной достигает заданного значения, выполнение цикла заканчивается.
Синтаксис этого вида цикла выглядит, следующим образом (в квадратные скобки заключены необязательные элементы синтаксической конструкции):
For <счетчик> = <начальноеЗначение>
То <конечноеЗначение>
[Step <приращение>]
<блокОператоров> Next [<счетчик>]
Несколько пояснений к приведенному описанию:

  • <приращение> — может быть как положительным, так и отрицательным числом. Если использовать отрицательное приращение, то конечное значение должно быть меньше либо равно начальному значению для того, чтобы тело цикла выполнилось хотя бы один раз;
  • после завершения работы цикла For. . .Next переменная, которая использовалась в качестве счетчика, получает значение, обязательно превосходящее конечное значение в том случае, если приращение положительно, и строго меньшее конечного значения, если приращение отрицательно;
  • если начальное и конечное значения совпадают, тело цикла выполняется лишь один раз.

Рассмотрим еще одну разновидность цикла For. . .Next, часто использующуюся в VBA при обработке объектов, составляющих массив или семейство однородных объектов. В этой разновидности цикла счетчик отсутствует, а тело цикла выполняется для каждого элемента массива или семейства объектов. Вот синтаксис такого цикла:
For Each <элемент> In <совокупность>
<блокОператоров>
Next [<элемент>]
где:
<элемент> — это переменная, используемая для ссылки на элементы семейства объектов;
<совокупность> — это имя массива или семейства.
Приведем пример использования подобного цикла. Следующая процедура предназначается для выдачи на печать списка всех полей для всех таблиц текущей открытой базы данных:
Public Sub EnumerateAllFields()
Dim MyBase As Database
Dim tdf As TableDef, fid As Field
Set MyBase = CurrentDb()
For Each tdf In MyBase.TableDefs
Debug.Print "Таблица: " & tdf.Name
For Each fid In tdf.Fields
Debug.Print " Поле: " & fid.Name Next fid
Next tdf
Set MyBase = Nothing
End Sub
Итак, в операторах Dim мы объявили переменную MyBase как объект "база данных DАО", переменные tdf и fid — как определение таблицы и поле таблицы, соответственно. Оператор Set назначает переменной MyBase текущую открытую базу данных. Далее для каждого определения таблицы выполняется вывод на печать названия таблицы, а затем вложенный цикл такого же типа печатает названия всех ее полей.
Приведем еще один пример использования подобного оператора цикла For Each. . .Next для обработки всех элементов многомерного массива. Пусть у нас имеется трехмерный числовой массив из 1000 элементов (размерами 10x10x10), который мы хотим заполнить случайными вещественными числами в диапазоне от 0 до 1. Если бы мы применяли обычные циклы For. . .Next со счетчиками, используя счетчики в качестве индексов элементов массива, то для решения этой задачи потребовалось бы написать три вложенных цикла For. . . Next:
Dim tArray{9, 9, 9) As Single
Dim i%, j%, k%
Randomize
For i=0 To 9
For j=0 To 9
For k=0 To 9
tArray(i, j, k) = Rnd()
Next k
Next j
Next i
На самом же деле достаточно всего одного цикла, если вместо циклов со счетчиками воспользоваться циклом For Each . . . Next:
Dim tArray(9, 9, 9) As Single
Dim elem As Variant
Randomize
For Each elem In tArray
elem = Rnd()
Next
Еще раз порекомендуем использовать отступы при записи циклов, так же, как и при записи операторов ветвления.