gifts2017

Обучение: Использование рекурсии при обращении к результатам выполнения запроса

Опубликовал GSoft. (GSoft) в раздел Программирование - Практика программирования

Коллеги! Не пожалейте несколько секунд нажав на плюсик, вам мелочь, а мне приятно)))))
Также оставляйте замечания, комментарии и пожелания.


В продолжение темы от O-Planet  
Мастер класс «O-Planet»: использование рекурсивных вычислений в 1С - http://infostart.ru/articles/82/?ref=8784

Пару лет назад столкнулся с тем, что в ПУБе в некоторых отчетах количество группировок ограничено 5 уровнями и это при списке из позиций кажется 12.  Честно говоря, как-то не вдохновило описывать 12 реальных циклов, решил написать рекурсию. Потом функция переросла в практический пример, который я привожу своим слушателям для демонстрации того, как реализовывать рекурсию при использовании запроса.

пример отчета с рекурсией вот тут -  http://infostart.ru/projects/827/?ref=8784
в примере также дана обучающая обработка по работе с запросом, очень будет полезна тем кто только начинает знакомится с таким объектом как "запросом"
конфигурация с примером вот тут
http://infostart.ru/projects/827/


В данном примере строится отчет на базе трех группировок которые определяются при открытии формы, таким для использования рекурсии в других целях достаточно изменить запрос и эту процедуру. Хотя при наличии времени возможно описать универсальную процедуру в которой можно предоставить пользователю на первом шаге выбрать из метаданных интересующие его объекты, а на втором - атрибуты этих объектов, которые и будут являться группировками запроса. Далее на основании этих данных автоматически формировать ТекстЗапроса.

 

Перем КолвоГруппировок;

//___________________________________________________*
Процедура Группировка(ИмяЗапроса, ОбщКолвоГруппировок, ТекущаяГруппировка, СписЗначГрупп, ИмяТаблицы) Далее



//___________________________________________________*
Процедура ПриОткрытии()
СЗРасш.ДобавитьЗначение("Документ", "Документ");
СЗРасш.ДобавитьЗначение("День", "День");
СЗРасш.ДобавитьЗначение("Неделя", "Неделя");
СЗРасш.ДобавитьЗначение("Месяц", "Месяц");
СЗРасш.ДобавитьЗначение("Квартал", "Квартал");
СЗРасш.ДобавитьЗначение("Год", "Год");

СЗГрупп.ДобавитьЗначение("Сотрудник", "Сотрудник");
СЗГрупп.ДобавитьЗначение("Блюдо", "Блюдо");
СЗГрупп.ДобавитьЗначение("Интервал", "Интервал");

Форма.СЗРасш.Видимость(0);
Форма.ИмяСЗРасш.Видимость(0);

КолвоГруппировок = СЗГрупп.РазмерСписка();
КонецПроцедуры




Непосредственно сам запрос. Параллельно в процедуре выполняется описание вспомогательных переменных для дальнейшей работы рекурсии
//___________________________________________________*
Процедура Сформировать()
Перем Запрос
, ТекстЗапроса, Таб;

ГруппировкаВыбрана = 0;
//определение выборки хотя бы одной группировки
Для Сч = 1 По КолвоГруппировок Цикл
Если
СЗГрупп.Пометка(Сч) = 1 Тогда
ГруппировкаВыбрана = 1;
КонецЕсли;
КонецЦикла;

Если ГруппировкаВыбрана = 0 Тогда
Предупреждение("Вы не указали ни одной группировки", 60);
Возврат;
КонецЕсли;

//определение указания расшифровки по интервалу
Если (СЗГрупп.Пометка(СЗГрупп.НайтиЗначение("Интервал"),) = 1) И (Форма.СЗРасш.Видимость() = 0) Тогда
Предупреждение("Вы не выбрали периодичность интервала", 60);
ВидимостьРасшифровки();
Возврат;
КонецЕсли;


//Создание объекта типа Запрос
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы Проведенные;
|Обрабатывать НеПомеченныеНаУдаление;
|Сотрудник = Документ.ЗаказСотрудника.Сотрудник;
|Блюдо = Документ.ЗаказСотрудника.Блюдо;
|Количество = Документ.ЗаказСотрудника.Количество;
|Стоимость = Документ.ЗаказСотрудника.Сумма;
|Функция КолСумма = Сумма(Количество);
|Функция СтоимСумма = Сумма(Стоимость);
|"//}}ЗАПРОС
;

//доп СЗ для вызова процедуры-рекурсии группировка
//у данного списка значений - две функции:
//во-первых, его размер будет говорить о том сколько раз будет запущена рекурсия
//Во-вторых, значения, помещенные в него - идентификаторы группировок к которым мы будем //обращаться в рекурсии
СЗГруппРек = СоздатьОбъект("СписокЗначений");

Для Сч = 1 По КолвоГруппировок Цикл
Если
СЗГрупп.Пометка(Сч) = 1 Тогда
Если
СЗГрупп.ПолучитьЗначение(Сч,) = "Интервал" Тогда
Интервал = СЗРасш.ПолучитьЗначение(СЗРасш.ТекущаяСтрока());
ТекстЗапроса = ТекстЗапроса + "Группировка " + Интервал + ";";

СЗГруппРек.ДобавитьЗначение(Интервал, Интервал);

Иначе
ЗначениеГруппировки = СЗГрупп.ПолучитьЗначение(Сч);
ТекстЗапроса = ТекстЗапроса + "Группировка " + ЗначениеГруппировки + ";";
СЗГруппРек.ДобавитьЗначение(ЗначениеГруппировки, ЗначениеГруппировки);
КонецЕсли;
КонецЕсли;
КонецЦикла;

// Если ошибка в запросе, то выход из процедуры
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат
;
КонецЕсли;

СписокОбъектов = "";

//Формируем заголовок колонок табличной части
Для Сч = 1 По КолвоГруппировок Цикл
Если
СЗГрупп.Пометка(Сч) = 1 Тогда

СЗГрупп.ПолучитьЗначение(Сч,СписокОбъектов);
Если Сч =1 Тогда
Заголовок = СписокОбъектов;
Иначе
Заголовок = Заголовок + "/" + СписокОбъектов;
КонецЕсли;
КонецЕсли;
КонецЦикла;

// Подготовка к заполнению выходных форм данными запроса
Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Сформировать");
// Заполнение полей "Заголовок"
Таб.ВывестиСекцию("Заголовок");
Состояние("Заполнение выходной таблицы...");
Таб.Опции(0, 0, Таб.ВысотаТаблицы(), 0);


//определяем количество циклов
ОбщКолвоРезГруппировок = СЗГруппРек.РазмерСписка();

//В этом месте по обычаю находились бы вложенные циклы с обходом по группировкам
//результатов выполнения запроса.
//Мы же здесь вызываем рекурсию. В процедуру передаем ссылку на сам запрос, для обращения
//к его результатам, общее количество группировок, номер первой группировки - так как
//здесь мы запускаем внешний цикл и таблицу в которую формируем отчет
Группировка(Запрос, ОбщКолвоРезГруппировок, 1, СЗГруппРек, Таб);


// Заполнение полей "Итого"
Таб.ВывестиСекцию("Итого");
// Вывод заполненной формы
Таб.ТолькоПросмотр(1);
Таб.Показать("Сформировать", "");

КонецПроцедуры



И вот собственно сама рекурсия. Процедура вызывается всегда с 4-мя параметрами.
1. Ссылка на запрос к которму мы обращаемся
2. Общее количество группировок.
3. Один из двух основных параметров - номер группировки к которой будет идти обращение
4. Второй основной параметр - список значений в котором содержатся в иерархическом порядке идентификаторы группировок к которым происходит обращение в данном вызове процедуры. Группировка определяется исходя из значения предыдущего параметра
5. И собственно говоря таблица в которую выводим значения

В принципе параметры 1,2 и 5 можно сделать общими для всего модуля, тогда их можно будет убрать из параметров процедуры

Общая идея процедуры такова - при входе в процедуру получаем идентификатор текущей группировки и запускаем по ней цикл. Здесь же обращаемся к атрибутам группировки и заполняем соответствующие переменные и выводим секции в таблицу.
Далее увеличиваем номер текущей группировки - то есть готовимся к обходу результатов по следующей (вложенной) группировке. Сравниваем полученный номер с общим количеством группировок и если он меньше то запускаем рекурсивную процедуру с теми же параметрами но уже с номером вложенной группировки и т.д. В самой вложенной группировке переменная ТекущаяГруппировка будет на единицу больше общего количества группировок, а посему рекурсию мы больше не запускаем. Уменьшаем номер группировки до текущего и переходим на следующий виток цикла. Как только мы доберемся до конца цикла мы просто выйдем из данной (самой вложенной) процедуры и вернемся в вышестоящую, там уменьшаем номер группировки до значения текущей на этом уровне группировки, и переходим на следующий виток цикла текущей группировки, где опять вызовем самую вложенную группировку и .д.
В конечном итоге после выполнения всех вложенных проццедур мы вернемся в процедуру Сформировать() где покажем заполненную в рекурсии таблицу, и закончим работу модуля.

//___________________________________________________*
//сама рекурсия
Процедура Группировка(ИмяЗапроса, ОбщКолвоГруппировок, ТекущаяГруппировка, СписЗначГрупп, ИмяТаблицы)
Пока
ИмяЗапроса.Группировка(ТекущаяГруппировка) = 1 Цикл

// Заполнение полей группировки
АтрибутГруппировки = "";
СписЗначГрупп.ПолучитьЗначение(ТекущаяГруппировка, АтрибутГруппировки);

Объект = ИмяЗапроса.ПолучитьАтрибут(АтрибутГруппировки);
СуммаПоОбъекту = ИмяЗапроса.КолСумма;
СтоимПоОбъекту = ИмяЗапроса.СтоимСумма;

Попытка
Если
Объект.ЭтоГруппа() = 1 Тогда
ИмяСекцииДляВывода = "Группа";
Иначе
ИмяСекцииДляВывода = "Группировка" + ТекущаяГруппировка;
КонецЕсли;
ИмяТаблицы.ВывестиСекцию(ИмяСекцииДляВывода);
Исключение
ИмяТаблицы.ВывестиСекцию("Группировка" + ТекущаяГруппировка);
КонецПопытки;

//увеличиваем номер группировки для входа во внутренний цикл
ТекущаяГруппировка = ТекущаяГруппировка + 1;

Если ТекущаяГруппировка <= ОбщКолвоГруппировок Тогда
Группировка(ИмяЗапроса, ОбщКолвоГруппировок, ТекущаяГруппировка, СписЗначГрупп, ИмяТаблицы);
КонецЕсли;

//уменьшаем номер группировки для входа во внешний цикл
ТекущаяГруппировка = ТекущаяГруппировка - 1;
КонецЦикла;

КонецПроцедуры

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Anonymous 14.03.07 11:53
Может я чего не понимаю, но зачем в качестве статьи выкладывать чистый код с минимумом комментариев? Достаточно было в комментарии к обработке написать, что в ней реализуется пример того-то и того-то
2. Brr (brr) 20.03.07 15:37
Откройте ТиС, посмотрите, не изобретайте велосипед, а ведь еще есть RegPrint.ert!
3. GSoft. (GSoft) 21.03.07 23:36
Да я и не старался ничего изобретать. Просто увидел статью на тему рекурсии и вспомнил про эту штуку,Выложил, может кому-нибудь понадобится.
4. Sasha_H (logarifm) 24.04.07 17:51
Интересное решение, рекурсия всегда была нелегкой и иттерация тоже ! +1 только за решение при этом в запросе.
5. GSoft. (GSoft) 24.04.07 23:59
6. GSoft. (GSoft) 08.08.07 11:54
2Brr

кстати, я тут вспомнил с чего это началось:))))
откройте типовую ПУБ, там есть отчет связанный с остатками, так вот там есть возможность выбора большого количества группировок, но МАКСИМАЛЬНОЕ возможное количество при построении отчета всегда ограничено 5 позициями, а надо было на тот момент 8. Ну а где 8, там и 10 :)))) и т.д.
7. Poppy (poppy) 10.08.07 01:37
(6)
Ты ПУБ не трогай. В ней все коряво написано.

В ТиС отчеты реализованы с помощью рекурсии, но в ПУБ с помощью вложенных циклов. При этом, в ТиС ограничение в 5 группировок, но в ПУБ - 8. И связано это не в способе реализации алгоритма, но в том, что в макете отчета имеется ограниченное количество секций.
8. GSoft. (GSoft) 13.08.07 12:57
2poppy

а кто мешает начиная, скажем, с 5 уровня вложенности использовать одну и ту же секцию, или использую динамические секции делать сдвиг по горизонтали

з.ы. да никто собстно пуб и не трогает просто описал историю появления на свет сабжа:-)
9. Poppy (poppy) 13.08.07 13:12
(8)
Ничто не мешает. Просто, в типовых конфигурациях это не реализовано. Проще ограничить количество одновременно используемых группировок.

З.Ы. В твоем коде это также не реализовано.
10. GSoft. (GSoft) 14.08.07 13:25
2poppy

не будем разводить демагогию и меряться кодерами :)))))
в этом коде это действительно не реализовано по одной просто причине, это учебный пример который я даю своим слушателям на 4-5 занятии в рамках курса "Программирование и конфигурирование 7.7"
Мало того что для них в это время запрос является довольно сложной вещью, а тут еще рекурсия и динамические секции. Слишком много информации. Да и тем более это пример я им просто показываю, иногда даже особо не вдаваясь в глубину рекурсии, а отдавая это на откуп тем кому это интересно, и уже с ними потом отдельно все разбираю.

Что же касается полного варианта - то у я ограничился 7 группировками, а дальше делал сдвиги, почему цифра именно такая - просто напросто метод научного тыка в рамках поставленной на тот момент задачи - такая разбивка дала самое оптимальное отображение для восприятия
11. Poppy (poppy) 14.08.07 14:20
(10) > не будем разводить демагогию и меряться кодерами :)))))

Никто ничем не меряется. Просто, решение, которое ты описал в своей статье неполностью решает задачу описанную в (6)

> Мало того что для них в это время запрос является довольно сложной вещью, а тут еще рекурсия и динамические секции. Слишком много информации.

Если информации много, может есть смысл разбить пример на несколько? Как говорится "от простого к сложному".
12. GSoft. (GSoft) 14.08.07 17:50
но ведь никто и не говорил что описанное мной в п (6) реализуется вышеизложенным кодом

от простого к сложному не получается, сейчас итак пришлось курс ужать до 96 академчасов, а я все еще продолжаю читать материал из расчета 120 академчасов, несмотря на то что и моя программа выходила далеко за рамки этих 120 часов. На самом деле пример приурочен к изучению запроса, чтобы слушатели знали что в 1С можно использовать рекурсию и понимали общий принцип использования. Что касается запроса, то для сокращения времени пришлось написать маленькую обработку на базе учебной конфигурации, в которой собран как теоретический материал, и так и практическая реализация запроса.
13. GSoft. (GSoft) 14.08.07 17:53
.... в которой собран как теоретический материал, и так и практическая реализация запроса.
Если интересно могу выложить. Хотя данная обработка представляет интерес для начинающих программистов.
14. Александр Медведев (anig99) 09.10.09 08:58
Чего-то меня настораживает уровень программистов 1с... Рекурсию и прочие СТАНДАРТНЫЕ приемы программирования мы проходили в школе...до 9 класса. Не вижу ничего сложного в рекурсии.
ИМХО что-то не то дают в институтах на курсах по программированию 1с. Сначала нужно стать программистом (ну хоть чуть-чуть, хотя думать как программист), а потом уже 1с...
З.Ы. Если уж пошла такая пляска может тогда выложить такие статьи:
1. Как поменять местами значение двух числовых переменных
2. Сортировка методом пузырька
3. Ускорения поиска значения делением массива
4. Разбиение чисел на десятки, сотни, тысячи и т.д.
5. Структурированый код - залог здоровья (как правильно использовать TAB)
Арчибальд; +1 Ответить 1
15. Александр Рытов (Арчибальд) 09.10.09 09:07
(14) В одноэсники идут не только программисты, но и кто попало. Последних - больше. Для них твои пункты 2 и 3 - уже запредельщина, пункт 1 - вне сферы интересов и т.д. Автора, занимающегося обучением таких персонажей, можно только пожалеть :D
16. Епрст (Ёпрст) 09.10.09 09:19
(0) А чего, типовые не кошерно глядеть?
Там всё это есть...
если что.
17. Михаил Ражиков (tango) 09.10.09 09:27
1.
a = a + b;
b = a - b;
a = a - b;
типа может кому интересно...
ну типа статья :))
18. Артур Аюханов (artbear) 09.10.09 10:45
Нормальная статья, что накинулись-то?
Как раз подобные статьи могут быть полезными для людей-непрограммистов, пришедших в 1С :)
19. Александр Медведев (anig99) 09.10.09 19:17
(18) непрограммисты не должны программировать...
и статья не раскрывает суть рекурсии... на минус конечно не тянет, но называть статью ТАК не правильно, и уж тем более 2 раза выкладывать одно и тоже... Мда....
20. GSoft. (GSoft) 13.10.09 10:37
(15) полностью согласен с Арчибальдом
не знаю как сейчас обстоит дело с 8-кой, но когда я преподавал программирование не 7-ке приходили люди которые и с операционной системой то плохо ладили))) не говоря уже о программировании, методах сортировки и т.д.))))

(19) если Вы внимательно посмотрите даты размещения на портале то увидите что это не вчера и даже не позавчера)))
а на тот момент не было возможности в статьях вставлять файлы а в программах описывать код, так что отсюда и две темы. Собстно у меня еще пара таких же ситуаций по выложенным мной материалам.
p.s. предложите свое название
21. Александр Медведев (anig99) 13.10.09 10:45
(20) не...ну по факту-то я тоже согласен, что 1с программистами называют кого попало...но ведь это не основание для утверждения, что так должно быть. Я ратую за то, чтобы на всяческих курсах преподавали элементарные основы программирования: структурирование кода, простейшие алгоритмы сортировки, особенности применения процедур и функций (в том числе и рекурсию), булева алгебра...
22. GSoft. (GSoft) 13.10.09 11:11
так кто же с этим спорит
но как ни печально но это факт, у меня к примеру сейчас в политехе(колледж) на третьем курсе студенты не знают что такое естественный алгоритмический язык, вот и говори после этого с ними о рекурсии, не говоря уже о системном моделировании которое я им читаю и которое должно базироваться на очень большом количестве фундаментальных вещей
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа