Разработка и Конфигурация · 10 мин
Какие бывают циклы в 1С и как их использовать
Разбираем виды циклов во встроенном языке 1С: Для…Цикл, Для Каждого…Из…Цикл, Пока…Цикл. Примеры кода, прерывание, типичные ошибки и выбор подходящего цикла.
Циклы во встроенном языке 1С нужны везде, где требуется повторить одни и те же действия: обойти строки табличной части документа, перебрать записи регистра, сформировать несколько печатных форм или накопить итог по коллекции. Без циклов практически невозможно написать ни один прикладной алгоритм — от простого расчёта суммы до сложной обработки данных.
В языке 1С существует три вида циклов: Для…Цикл (со счётчиком), Для Каждого…Из…Цикл (обход коллекции) и Пока…Цикл (с условием). У каждого своя область применения, свои ограничения и типичные ошибки. Неправильный выбор цикла приводит либо к лишнему коду, либо к бесконечному выполнению и зависанию сеанса.
Ниже разберём синтаксис каждого вида, покажем рабочие примеры на встроенном языке, объясним, как прерывать и пропускать итерации, и дадим критерии выбора подходящей конструкции.
Цикл Для…Цикл: повторения с известным счётчиком
Конструкция Для…Цикл применяется, когда заранее известно количество итераций или нужен числовой счётчик. Переменная-счётчик автоматически увеличивается на единицу при каждом проходе, шаг изменить нельзя. Синтаксис:
Для Счетчик = 1 По 10 Цикл
Сообщить("Итерация № " + Счетчик);
КонецЦикла;
Границы цикла вычисляются один раз перед началом выполнения. Если внутри тела цикла изменить переменную, задавшую верхнюю границу, это не повлияет на количество итераций — цикл отработает исходный диапазон. Это важно учитывать, чтобы не получить неожиданное поведение.
Типичный прикладной сценарий — обход табличной части по индексу, когда нужен номер строки:
Для Индекс = 0 По ТабличнаяЧасть.Количество() - 1 Цикл
СтрокаТЧ = ТабличнаяЧасть[Индекс];
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецЦикла;
Обратите внимание: индексация коллекций в 1С начинается с нуля, поэтому верхняя граница — Количество() - 1. Ошибка «на единицу» здесь — самая частая причина исключения «Индекс находится за границами массива».
Цикл Для Каждого…Из…Цикл: обход коллекций
Конструкция Для Каждого…Из…Цикл — основной рабочий инструмент в прикладном коде. Она перебирает элементы коллекции: массива, структуры, таблицы значений, табличной части, выборки результата запроса, списка значений. На каждой итерации переменная цикла получает ссылку на очередной элемент.
Для Каждого СтрокаТовара Из Объект.Товары Цикл
Если СтрокаТовара.Количество = 0 Тогда
Сообщить("В строке " + СтрокаТовара.НомерСтроки + " не указано количество");
КонецЕсли;
КонецЦикла;
Тот же подход работает с выборкой запроса — это стандартный способ обработки данных из базы:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// обработка текущей записи выборки
КонецЦикла;
Здесь важный нюанс: выборка запроса обходится через метод Следующий() в цикле Пока, а не через Для Каждого. Хотя Для Каждого по выборке формально выполнится, идиоматически правильный и предсказуемый способ — именно Пока Выборка.Следующий().
Обход структуры и соответствия
При обходе структуры или соответствия переменная цикла содержит объект КлючИЗначение, у которого есть свойства Ключ и Значение:
Для Каждого Элемент Из СоответствиеКурсов Цикл
Сообщить(Элемент.Ключ + ": " + Элемент.Значение);
КонецЦикла;
Цикл Пока…Цикл: повторения по условию
Конструкция Пока…Цикл выполняется, пока условие истинно. Условие проверяется перед каждой итерацией, поэтому если оно ложно с самого начала — тело цикла не выполнится ни разу:
Пока Выборка.Следующий() Цикл
ИтоговаяСумма = ИтоговаяСумма + Выборка.Сумма;
КонецЦикла;
Этот цикл выбирают, когда число повторений заранее неизвестно: чтение файла построчно, движение по выборке запроса, итеративные расчёты до достижения точности, ожидание освобождения ресурса.
Главный риск — бесконечный цикл. Если условие никогда не станет ложным, сеанс зависнет, а на сервере процесс будет потреблять ресурсы до принудительного завершения. Всегда проверяйте, что внутри тела цикла есть действие, которое рано или поздно изменит условие.
Управление выполнением: Прервать и Продолжить
Два оператора управляют ходом любого цикла. Прервать немедленно завершает цикл целиком и передаёт управление на следующую за ним строку. Продолжить досрочно завершает текущую итерацию и переходит к следующей.
Для Каждого Строка Из ТаблицаДанных Цикл
Если Не ЗначениеЗаполнено(Строка.Номенклатура) Тогда
Продолжить; // пропускаем пустые строки
КонецЕсли;
Если Строка.ЭтоСтопПризнак Тогда
Прервать; // дальше обрабатывать не нужно
КонецЕсли;
ОбработатьСтроку(Строка);
КонецЦикла;
Оба оператора действуют только на тот цикл, в котором находятся непосредственно. Если нужно прервать внешний цикл из вложенного, стандартного «меченого» прерывания в 1С нет — используют флаг-переменную или выносят вложенный цикл в отдельную функцию с Возврат.
Как выбрать подходящий цикл
Выбор определяется источником данных и тем, что известно заранее. Ориентируйтесь на простые правила:
Как выбрать действие
| Критерий | Для…Цикл | Для Каждого…Цикл | Пока…Цикл |
|---|---|---|---|
| Число итераций | Известно заранее | Равно размеру коллекции | Неизвестно |
| Типичный источник | Счётчик, диапазон | Коллекция | Выборка, файл, условие |
| Риск бесконечного цикла | Нет | Нет | Да, при ошибке в условии |
| Доступ к индексу | Есть напрямую | Через отдельный счётчик | Через отдельный счётчик |
Если при обходе коллекции нужен и элемент, и его порядковый номер, заведите отдельную переменную-счётчик и увеличивайте её внутри Для Каждого — это читабельнее, чем переписывание на цикл по индексу.
Вложенные циклы и производительность
Циклы можно вкладывать друг в друга без ограничений по синтаксису, но каждый уровень вложенности умножает количество итераций. Два цикла по тысяче элементов дают миллион проходов внутреннего тела — на слабом сервере или при обращениях к базе внутри цикла это заметно.
Главное правило производительности: не выполняйте запросы к базе данных внутри цикла. Классическая ошибка новичка — в цикле по строкам документа каждый раз запрашивать цену или остаток отдельным запросом. Правильный подход — один запрос с соединением или временной таблицей до цикла, а в цикле только поиск по уже полученной таблице значений.
Если внутри цикла нужно многократно искать строки в таблице значений, заранее создайте по ней индекс (метод Индексы.Добавить) или преобразуйте её в соответствие — поиск по ключу соответствия работает значительно быстрее перебора НайтиСтроки на каждой итерации.
Проверка результата работы цикла
После написания цикла убедитесь, что он работает корректно на граничных случаях: пустая коллекция, один элемент, максимально реальный объём. Пустая коллекция не должна вызывать ошибок — циклы Для Каждого и Пока в этом случае просто не выполнятся, что обычно и требуется.
Как проверить результат
Цикл не выполняется, ошибок нет
Ошибка обращения по индексу или исключение
Совпадает с размером коллекции
Меньше — где-то срабатывает Прервать или пропуск при удалении
Линейно растёт от числа элементов
Резко растёт из-за запросов внутри цикла
Условие становится ложным за конечное число шагов
Сеанс зависает — условие не изменяется
Для отладки удобно поставить точку останова внутри тела цикла и в конфигураторе наблюдать значения переменной цикла и счётчиков на каждой итерации. Для больших коллекций ставьте условную точку останова, чтобы остановиться только на проблемном элементе.
Что важно запомнить
Три цикла 1С решают три задачи: Для — счётчик, Для Каждого — коллекции, Пока — условие и выборки. Прервать и Продолжить управляют потоком. Не изменяйте коллекцию во время её обхода и не выполняйте запросы к базе внутри цикла.
Частые вопросы о циклах в 1С
Можно ли изменить шаг цикла Для, например на 2 или на -1?
Нет, шаг всегда равен единице и изменить его нельзя. Если нужен другой шаг или обратный обход, используйте цикл Пока с собственным счётчиком, который вы изменяете внутри тела цикла так, как требуется.
Как прервать сразу два вложенных цикла?
Специального оператора для этого нет. Обычно заводят переменную-флаг: внутренний цикл устанавливает её и прерывается, а внешний проверяет флаг после внутреннего цикла и тоже выполняет Прервать. Альтернатива — вынести логику в функцию и выйти из неё оператором Возврат.
Что быстрее: Для Каждого или Для по индексу?
Для обхода коллекций целиком Для Каждого удобнее и обычно не медленнее. Цикл по индексу оправдан, только когда нужен сам индекс или требуется обход в нестандартном порядке. На практике узким местом почти всегда оказываются обращения к базе, а не вид цикла.
Почему при удалении строк в цикле пропускаются элементы?
При удалении элемента индексы остальных сдвигаются, а цикл переходит к следующей позиции — один элемент оказывается пропущенным. Решение: собрать удаляемые элементы в отдельный массив внутри цикла, а удалять их уже после завершения обхода.
Как обойти таблицу значений в обратном порядке?
Используйте цикл Пока со счётчиком, который уменьшается от Количество() - 1 до нуля, либо отсортируйте таблицу в обратном порядке и обойдите её обычным циклом Для Каждого. Цикл Для с отрицательным шагом в 1С отсутствует.
Можно ли использовать цикл внутри запроса?
Нет, язык запросов 1С не содержит циклов — он работает с наборами данных декларативно. Повторяющуюся логику в запросах реализуют соединениями, группировками и итогами, а обход результата выполняют уже на встроенном языке через выборку.