Использование рекурсивных вычислений в 1с

Из курса программирования как мы знаем, что рекурсией именуется вызов функцией самой себя. Практически, рекурсия есть первым шагом к логическому программированию, по причине того, что грамотное ее применение разрешает разработать метод, не решая задачи совсем.

Источник: http://www.o-planet-1c.narod.ru Как решить задачу рекурсивно? Запомним несложную теорему: задачу, имеющую рекурсивное ответ, неизменно возможно сформулировать индуктивно. Покажу это на несложном примере.

Допустим, мы ищем путь из точки А в точку В в лабиринте. Мы были на следующем перепутье. Перед нами пара направлений вероятного пути. Что будет, в случае если мы выберем одно из направлений?

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

И без того , пока мы не придем в конечную точку. Так, непростая задача поиска пути в лабиринте была сведена к однотипным операциям выбора в каждой очередной его точке. Метод рекурсивного ответа данной задачи будет смотреться приблизительно так:

Функция ПоискПути(Х) В случае если Х=А Тогда Возврат 1; // Финиш пути достигнут тут КонецЕсли; До тех пор пока Есть_Возможные_Пути(Х)=1 Цикл У=Очередная_Точка(Х); В случае если ПоискПути(У)=1 Тогда Возврат 1; // Финиш пути был достигнут где-то в том месте КонецЕсли; КонецЦикла; Возврат 0; // Тупик, либо не осталось вариантов КонецФункции

Но возвратимся к теме [advert=103]1С[/advert]. Неужто рекурсию возможно применять и тут удачно, спросит дока? Нужно подчернуть, что у многих рекурсивных методов имеется значительный недочёт: они ресурсоемки и достаточно медленны если сравнивать с простыми циклами, исходя из этого, вопрос звучит в полной мере уместно.

Несомненно, приведенный пример возможно решить и не прибегая к рекурсивным вызовам. Но время от времени без них не редкость практически не обойтись.

Разглядим задачу, ответ которой для отдельных бухгалтеров может стать выполнением одного из их заветных жажд. Попытаемся привязать долги клиентов к конкретным расходным накладным в программе 1С:Бухгалтерия 7.7.

Несомненно, решить эту задачу в лоб – свидетельствует, пересчитать всю базу В первую очередь времен, поскольку любой агент, фактически говоря, может что-то брать, позже частично это оплачивать, позже брать что-то еще и т.д.

Будем решать эту задачу в обратном направлении. Отыщем в памяти, что способ FIFO, по большому счету говоря, обязан распространяться и на оплату. Иными словами, в случае если от какого-либо клиента поступает оплата, то ни что не мешает нам привязать ее к самому раннему неоплаченному документу по этому клиенту.

Итак, в случае если клиент А задолжал нам 1000 рублей, то возможно с уверенностью вычислять неоплаченными последние N документов, по которым он что–или у нас купил на сумму, не меньше, чем 1000 рублей.

Что ж, будем выбирать все операции по агентам в обратном порядке, разнося по ним сумму имеющегося долга. Очевидно, это было бы стремительнее, чем выбирать и пересчитывать все документы В первую очередь.

Выстроим рекурсивную обработку операций. Будем разглядывать операции отрезками в 31 сутки.

Функция ПолучитьДокументы(Агент,Т,Знач Д1,Знач Д2,СальдоКон=0) // Т – таблица значений, которая заполняется документами на выходе рекурсии // Д1 и Д2 – очередной разглядываемый период // СальдоКон – сальдо по агенту на конец периода. Значение в // самом начале равняется нулю. В будущем оно будет расчитано

Составим запрос по операциям агента за месяц

Ит=СоздатьОбъект(БухгалтерскиеИтоги); Ит.ИспользоватьСубконто(ВидыСубконто.Агенты,Агент,2,0); В случае если Ит.ВыполнитьЗапрос(Д1,Д2,62.1,,,1,операция,С)=0 Тогда Сказать(Неточность!); Возврат 0; КонецЕсли;

Определим условие завершения рекурсии

В случае если СальдоКон=0 Тогда СальдоКон=Ит.СКД(С)-Ит.СКК(С); КонецЕсли; В случае если СальдоКон
Нам пригодится запасной таблица значений

Таб=СоздатьОбъект(ТаблицаЗначений); Таб.НоваяКолонка(Приход,Число,15,2); Таб.НоваяКолонка(Дата,Дата); Таб.НоваяКолонка(Позиция); Таб.НоваяКолонка(Документ,Документ);

Заполним таблицу отысканными в запросе операциями

Ит.ВыбратьПериоды(); До тех пор пока Ит.ПолучитьПериод()=1 Цикл Таб.НоваяСтрока(); Таб.Приход=Ит.ДО(С); Таб.Документ=Ит.Операция.Документ; Таб.Дата=Ит.Операция.Документ.ДатаДок; Таб.Позиция=Ит.Операция.Документ.ПолучитьПозицию(); КонецЦикла;

В случае если в текущем месяце не было докуменетов, то разглядим месяц ранее по рекурсии. Это будет косвенное условие завершения.

В случае если Таб.КоличествоСтрок()=0 Тогда Возврат ПолучитьДокументы(Агент,Т,Д1-31,Д1-1,СальдоКон); КонецЕсли;

В случае если полученная таблица операций не безлюдна, то упорядочим ее по убыванию позиции документов

Таб.Сортировать(-Позиция);

Разнесем долг агента по документам, формирующим данный долг. В тот момент, в то время, когда остаток долга станет меньше нуля, будем вычислять, что долг всецело привязан к документам.

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

В случае если документы в текущем месяце не покрыли долг, то берем в рассмотрение еще один месяц рекурсивно.

В случае если СальдоКон0 Тогда Возврат ПолучитьДокументы(Агент,Т,Д1-31,Д1-1,СальдоКон); КонецЕсли;

В случае если покрыли, то возвращаем 1

Возврат 1; КонецФункции

Думаю, сейчас любой легко осознает, как применять эту функцию по большей части расчетном модуле. Приведу примерный, упрощенный вариант.

Таблица=СоздатьОбъект(«Таблица»); Таблица.ВывестиСекцию(«Шапка»); Т=СоздатьОбъект(«ТаблицаЗначений»); Т.НоваяКолонка(«Документ»); Т.НоваяКолонка(«Дата»); Т.НоваяКолонка(«Долг»); Т.НоваяКолонка(«ОстатокДолга»); Агенты=СоздатьОбъект(«Справочник.Агенты»); Агенты.ВыбратьЭлементы(); До тех пор пока Агенты.ПолучитьЭлемент()=1 Цикл Д=РабочаяДата(); Т.УдалитьСтроки(); ПолучитьДокументы(Агенты.ТекущийЭлемент(),Т,Д-30,Д); ВывестиВТаблицу(Таблица,Агенты.ТекущийЭлемент(),Т); КонецЦикла; Таблица.ВывестиСекцию(«Подвал»); Таблица.Опции(0,0,0,0,«ДолгиКонтрагентов1»,«ДолгиКонтрагентов2»); Таблица.ТольоПросмотр(1); Таблица.Продемонстрировать(«Долги»);

Мы разглядели упрощенный вариант задачи, предполагая использование способа FIFO к оплате, и как видите, рекурсия в приведенном случае в полной мере оправдана. Несомненно, больший интерес воображает метод, в котором учитывается как дебетовые, так и кредитовые обороты по счету 62, и в этом случае, на выходе мы можем взять что-то наподобие книги приобретений, но без применения забалансовых квитанций.

Что ж, дерзайте, коллеги!

Приведенный метод применен в отечественных обработках Кто нам обязан и Кому мы должны

Стань специалистом
1C 1С
Рейтинг публикаций
Цены Нативная реклама Связаться
Мнения
Клерк (0) Facebook Вконтакте
Добавить

  • Цитировать
  • Ссылка на фрагмент

Люди которым это нравится

Закрыть

Работа с массивом в 1С: вычисляем сумму элементов массива


Понравилась статья? Поделиться с друзьями: