Разбор от сообщества: Как обойти дерево значений | infolimp.ru

Разбор от сообщества: Как обойти дерево значений

12 августа 2026 · infolimp.ru

Обход дерева значений — задача, с которой сталкивается каждый разработчик 1С. Казалось бы, что может быть проще: рекурсивная процедура, вызов ПолучитьСтроки() — и готово. Но на практике именно этот «простой» подход приводит к неожиданным падениям в рабочих базах, когда глубина иерархии превышает 50–100 уровней. Разберём, почему рекурсия опасна, как заменить её итеративным обходом без потери производительности, и в каких случаях от дерева значений лучше отказаться вовсе.

Рекурсивный обход: классика и её подводные камни

Как устроено дерево значений

ДеревоЗначений в платформе 1С — это коллекция строк, каждая из которых может содержать вложенные строки (свойство Строки). Внутренняя реализация — связный список с ссылками на родителя и потомков. При рекурсивном обходе мы последовательно обращаемся к Строки.Количество(), затем для каждой строки вызываем ту же процедуру. На первый взгляд — элегантно.

Процедура ОбойтиРекурсивно(СтрокиДерева)
    Для Каждого ТекСтрока Из СтрокиДерева Цикл
        // Обработка строки
        Сообщить(ТекСтрока.Имя);
        // Рекурсивный вызов для подчинённых
        ОбойтиРекурсивно(ТекСтрока.Строки);
    КонецЦикла;
КонецПроцедуры;

// Вызов
ОбойтиРекурсивно(Дерево.Строки);

Ключевой тезис: Рекурсия в 1С не оптимизирована под глубокие иерархии. Каждый вложенный вызов — это новый фрейм стека, а платформа выделяет под стек ограниченный объём памяти (обычно около 1–2 МБ). При глубине более 100–200 уровней вы гарантированно получите исключение «Переполнение стека».

Ошибка переполнения стека

Проблема проявляется не сразу. В тестовой базе с 10–20 уровнями всё работает. Но когда в боевом контуре появляется справочник «Классификатор товаров» с вложенностью 300+ строк, рекурсивный обход падает с Ошибка выполнения: Стек вызовов переполнен. Причём отловить это исключение стандартной конструкцией Попытка…Исключение не всегда удаётся — платформа может аварийно завершить сеанс.

Дополнительная ловушка: рекурсия замедляется экспоненциально, если внутри цикла выполняются «тяжёлые» операции (запросы, запись в регистры). Каждый уровень добавляет накладные расходы на вызов процедуры.

Итеративный обход с явным стеком

Реализация на основе массива

Вместо рекурсии используем ручное управление стеком. Стек — это обычный массив, в который мы складываем строки, которые нужно обработать. Алгоритм:

  1. Помещаем в стек корневые строки (в обратном порядке, чтобы сохранить порядок обхода).
  2. Пока стек не пуст: извлекаем строку, обрабатываем, затем добавляем её подчинённые строки в стек (снова в обратном порядке).
Процедура ОбойтиИтеративно(Дерево)
    Стек = Новый Массив;
    
    // Заполняем стек корневыми строками в обратном порядке
    Для Инд = Дерево.Строки.Количество() - 1 По 0 Цикл -1
        Стек.Добавить(Дерево.Строки[Инд]);
    КонецЦикла;
    
    Пока Стек.Количество() > 0 Цикл
        ТекСтрока = Стек[Стек.ВГраница()];
        Стек.Удалить(Стек.ВГраница());
        
        // Обработка строки
        Сообщить(ТекСтрока.Имя);
        
        // Добавляем подчинённые строки в обратном порядке
        Для Инд = ТекСтрока.Строки.Количество() - 1 По 0 Цикл -1
            Стек.Добавить(ТекСтрока.Строки[Инд]);
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры;

Примечание: Порядок обхода при использовании стека — «в глубину» (DFS), но с обратным порядком добавления мы получаем тот же порядок, что и при рекурсии (сначала первый потомок, затем его поддерево). Если нужен обход «в ширину» (BFS), используйте очередь (например, СписокЗначений с добавлением в конец и извлечением из начала).

Сравнение подходов

ХарактеристикаРекурсияИтеративный стек
Глубина иерархииОграничена (обычно < 200)Не ограничена (только память)
СкоростьМедленнее из-за накладных расходов на вызовыБыстрее (один цикл)
Читаемость кодаВысокаяСредняя (требуется комментарий)
Риск переполнения стекаВысокийОтсутствует

Альтернативы: когда дерево не нужно

Таблица значений с иерархией

Если данные хранятся в справочнике или регистре сведений, а не формируются динамически, часто выгоднее использовать плоскую таблицу значений с колонкой «Родитель». Обход такой таблицы выполняется одним запросом (например, ВЫБРАТЬ ... ИЗ ... ГДЕ Родитель = &Текущий) или рекурсивным запросом через ОБЪЕДИНИТЬ ВСЕ. Это избавляет от проблем с глубиной и даёт возможность использовать индексы.

// Пример рекурсивного запроса (платформа 8.3.10+)
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    Ссылка,
|    Наименование,
|    Родитель
|ИЗ
|    Справочник.Номенклатура
|ГДЕ
|    Родитель = &Текущий
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|    Ссылка,
|    Наименование,
|    Родитель
|ИЗ
|    Справочник.Номенклатура
|ГДЕ
|    Родитель В (ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура КАК Влож ГДЕ Влож.Родитель = &Текущий)";
// ... и так далее до нужной глубины (обычно 10-15 уровней)

Замечание: Рекурсивные запросы через ОБЪЕДИНИТЬ ВСЕ имеют ограничение на количество объединений (обычно 256). Для очень глубоких иерархий используйте временные таблицы и цикл.

Использование СписокЗначений с группировкой

Если дерево нужно только для отображения в форме, рассмотрите СписокЗначений с пометкой групп. Это легковеснее и не требует рекурсивного обхода для получения плоского списка. Однако для программной обработки вложенности такой подход менее удобен.

Практические рекомендации

Чек-лист: что проверить перед обходом дерева

Типичные ошибки

  1. Расширение «НОПик» для 1С — встраиваемый коннектор к внешнему AI с интеллектуальным поиском по базе. Задавайте вопросы обычными словами - AI сам найдёт нужное. 45 дней бесплатно.

    Попробовать НОПик →
    Знаете ответ на такие вопросы не хуже автора статьи? Пройдите бесплатную анонимную проверку уровня на infolimp.ru - 3 практических задачи, 15 минут, публичный токен-профиль, который можно показать работодателю или заказчику. Без регистрации по почте.

    Проверить свой уровень бесплатно →