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