Имя: Пароль:
1C
1C 7.7
v7: Рекурсивная функция
,
0 kloptula
 
18.10.12
23:44
Есть вот такая функция, которая проверяет наличие строки в таблице значений и возвращает 1 в случае нахождения

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

Так вот, эта функция возвращает не то, что должна. Например: нашлась строка с заданными реквизитами и функция должна вернуть 1, но если смотреть в отладчике, то сначала действительно возвращается 1, а вот потом рекурсия возвращается на предыдущий шаг и берет и возвращает 0. Че за х..ня не пойму ни как. Может сталкивался кто-нибудь с такой засадой
1 Лефмихалыч
 
18.10.12
23:49
как вызываешь ее?
2 HeroShima
 
18.10.12
23:51
А зачем рекурсия?
3 Лефмихалыч
 
18.10.12
23:53
(2) видать холодно в кабинете, решил отопить путем нагрузки процессора
4 kloptula
 
18.10.12
23:54
(1) Вызываю вот так

                       Если НайтиЗначениеВТаблице(ТЗИтоговДоп, Запрос.Номенклатура, Запрос.Серия, Запрос.ЦенаПрод, НомСтр) = 0 Тогда
                           Продолжить;
                       КонецЕсли;
5 HeroShima
 
18.10.12
23:54
(3) не иначе
6 hhhh
 
18.10.12
23:56
(4) но ведь она у тебя ничего и не возвращает. Юморист. До пятницы еще несколько минут.
7 HeroShima
 
18.10.12
23:56
(4) чувствуешь?
8 kloptula
 
18.10.12
23:58
(6) Если ты про "продолжить", то после этого идет вывод строки в отчет в цикле, а я вывод этой строки пропускаю.
9 kloptula
 
19.10.12
00:00
(6)

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

                   ТЗ.НоваяСтрока();                              
ТЗ.Уровень = 0;
ТЗ.ТекРасшифровка    = ТМЦ;
ТЗ.Родитель            = ТМЦ.Родитель;
ТЗ.ПечЕд            = ?(ВидЕдиницы = 1,ТМЦ.ОсновнаяЕдиница, ТМЦ.БазоваяЕдиница);
                   ТЗ.ПечТекстСтроки    = ТМЦСтрока + ?(ТМЦ.МинОстаток=0,"",", мин. остаток = "+СокрЛП(глФРМКоличество(ТМЦ.МинОстаток,ТЗ.ПечЕд)));
                   Если ВыводитьЗаказанный = 1 Тогда
                       ТЗ.Заказано = Запрос.ЗаказаноКонОст;
                   КонецЕсли;
                   ЗаполнитьСтроку(ТЗ, Запрос, СписокСкладов, ВДокумент);
               КонецЦикла;
10 HeroShima
 
19.10.12
00:00
(8) он про не возвращает, но тебе и на это там наплевать
11 Лефмихалыч
 
19.10.12
00:02
(9) *баный стыд... а не код. Как ты это говно читаешь?
12 kloptula
 
19.10.12
00:02
(10) Как правильно возвращать результат в рекурсивной функции в 1С?
13 kloptula
 
19.10.12
00:03
(11) Да это форум перековеркал. А код большей частью из ТиС типового
14 hhhh
 
19.10.12
00:04
(12) чему то присвоить. Переменной какой нибудь. АП то у тебя функция выполняется просто так, результат куда-то в небо передаешь.
15 HeroShima
 
19.10.12
00:05
(12) правильно возвращать значение по умолчанию вне условий, а затем ещё и проверять что вернули
16 hhhh
 
19.10.12
00:06
(14) вот тут например

      Если (ТЗ.СерияНоменклатуры = Серия) и (ТЗ.ЦенаПрод = ЦенаПрод) и ((ТЗ.ДатаПартии - ДатаПоступления) < 0) Тогда
           Возврат 1;
       Иначе    
           НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки+1);
       КонецЕсли;

вот где у тебя результат, ты говоришь, что 1. А он ведь в воздухе висит. Никуда потом не присваивается
17 kloptula
 
19.10.12
00:08
(16) Вот так сделал. Один фиг тоже самое

Функция НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки)
                               
   // отфильтруем по дате поступления
   ТЗ.ПолучитьСтрокуПоНомеру(НомерСтроки);
   Если ТЗ.Номенклатура = Номенклатура Тогда
       Если (ТЗ.СерияНоменклатуры = Серия) и (ТЗ.ЦенаПрод = ЦенаПрод) и ((ТЗ.ДатаПартии - ДатаПоступления) < 0) Тогда
           Результат = 1;
       Иначе    
           НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки+1);
       КонецЕсли;
   Иначе
       Результат = 0;
   КонецЕсли;  
   
   Возврат Результат;
   
КонецФункции
18 kloptula
 
19.10.12
00:09
Или глобальную переменную объявлять для возврата значения, но это же некрасиво
19 HeroShima
 
19.10.12
00:09
(17) Кому ты вернул результат? И убери рекурсию - за неё тут расстрелять нужно даже за работающую.
20 Лефмихалыч
 
19.10.12
00:10
Возврат НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки+1);
блеать
21 Лефмихалыч
 
19.10.12
00:10
но лучше разбег, стена, конец карьеры
22 kloptula
 
19.10.12
00:11
(20) Бл.. генитально

Функция НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки)
                               
   // отфильтруем по дате поступления
   ТЗ.ПолучитьСтрокуПоНомеру(НомерСтроки);
   Если ТЗ.Номенклатура = Номенклатура Тогда
       Если (ТЗ.СерияНоменклатуры = Серия) и (ТЗ.ЦенаПрод = ЦенаПрод) и ((ТЗ.ДатаПартии - ДатаПоступления) < 0) Тогда
           Возврат 1;
       Иначе    
           Возврат НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки+1);
       КонецЕсли;
   Иначе
       Возврат 0;
   КонецЕсли;  
   
КонецФункции
23 kloptula
 
19.10.12
00:12
(20 Спасибо дружище
24 kloptula
 
19.10.12
00:15
(19) А без рекурсии получается громоздкий код, а с рекурсией красота
25 HeroShima
 
19.10.12
00:22
(24) не вижу там громоздкости
26 kloptula
 
19.10.12
00:24
(25) Где там?
27 HeroShima
 
19.10.12
00:25
(26) в умозрительно трансформированном коде
28 kloptula
 
19.10.12
00:28
Изобрази, если не сложно
29 HeroShima
 
19.10.12
00:30
(28) один малюсенький цикл
30 HeroShima
 
19.10.12
00:31
Очень надеюсь что тема - шутка.
31 kloptula
 
19.10.12
00:31
(29) ну....
32 HeroShima
 
19.10.12
00:34
(31) поверь на слово
33 GANR
 
19.10.12
00:44
(31)(0)Замени-ка ты эту рекурсию на массив (стэк) Как в такой ситуации заменить рекурсию на стэк ??? - прерви поиск, где тебе надо и всё пучком. Этот способ гораздо гибче.
34 GANR
 
19.10.12
00:48
(0) А в сабже замени это
НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки+1);
на это
Возврат НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки+1);
Неудивительно, что она 0 возвращает
35 Semen
 
19.10.12
03:09
(19) Тоже не понял, почему бы просто не перебрать в цикле ТЗ до момента позиционирования на записи удовлетворяющей условию
36 Stillcat
 
19.10.12
06:46
Да, слов нет.
(24) Вот твой "громоздкий код"

Функция НайтиЗначениеВТаблице(ТЗ, Номенклатура, Серия, ЦенаПрод, НомерСтроки)
 ТЗ.ВыбратьСтроки();
 Пока ТЗ.ПолучитьСтроку()=1 Цикл
   Если (ТЗ.Номенклатура = Номенклатура) и (ТЗ.СерияНоменклатуры = Серия) и (ТЗ.ЦенаПрод = ЦенаПрод) и ((ТЗ.ДатаПартии - ДатаПоступления) < 0) Тогда
     НомерСтроки=ТЗ.НомерСтроки; //-Если нужно
     Возврат 1;
   КонецЕсли;
 КонецЦикла;
 Возврат 0;
КонецФункции
37 Stillcat
 
19.10.12
06:49
В рекурсии конечно великая сила,
но в данном случае - из пушки по воробьям.
38 kloptula
 
19.10.12
07:43
(36) Работает очень медленно по сравнению с моим вариантом. Лобовое решение, но не оптимальное
39 Simod
 
19.10.12
07:55
(38) Оно не может работать медленнее, потому как ПолучитьСтроку() работает быстрее, чем ПолучитьСтрокуПоНомеру()

Функции в (0) в принципе нерабочая, т.к. нет проверки на выход из диапазона строк (НомерСтроки > ТЗ.КоличествоСтрок())

Почитайте какие-нибудь книжки по программированию.
40 VladZ
 
19.10.12
08:00
(0) Я бы использовал в таком случае "ИндексированнуюТаблицу". Условия вида: (ТЗ.СерияНоменклатуры = Серия) и (ТЗ.ЦенаПрод = ЦенаПрод) фильтруется на ура. Условие (ТЗ.ДатаПартии - ДатаПоступления) < 0 обрабатывать перебором по отфильтрованной ТЗ.
41 kloptula
 
19.10.12
08:12
(39) А Вы попробуйте сравнить. ПолучитьСтроку() работает с выборкой всей таблицы, и пока доберешься до нужной строки можешь перебрать почти всю таблицу. А найтиЗначение() и потом ПолучитьСтрокуПоНомеру() позиционирует сразу на нужную строку в таблице.

Иногда лучше жевать, чем говорить
42 dk
 
19.10.12
08:18
если действительно нужна скорость, то индексы в ТЗ спасут
43 ADirks
 
19.10.12
08:20
(41) если надо высокую скорость, то можно и посложней методы применить. Например сортировка + дихотомический поиск.
Ну или сразу ИТ.
44 kloptula
 
19.10.12
08:24
(42) Согласен, но код сложнее будет, т. к. придется сначала таблицу "переколбасить", а по производительности меня вариант с рекурсией вполне устроил.
45 ADirks
 
19.10.12
08:28
(44) Код будет сложнее всего 1 раз. Есть же такая штука, "повторное использование кода".

Например:

//_____________________________________________________________________________
Функция ЗначениеДляСравнения(Значение, ПоВнутрПредставлению)
   Если ПоВнутрПредставлению = 1 Тогда
       Возврат ЗначениеВСтрокуВнутр(Значение);
   Иначе
       Если ТипЗначения(Значение) = 12 Тогда
           Возврат Значение.ПолучитьПозицию();
       КонецЕсли;
       Возврат Строка(Значение);
   КонецЕсли;
КонецФункции

//_____________________________________________________________________________
//Возвращает число:
// 0 - ЗначениеКлюча = ЗначениеТЗ
// 1 - ЗначениеКлюча > ЗначениеТЗ
// -1 - ЗначениеКлюча < ЗначениеТЗ
Функция СравнитьСКлючом(Ключ, ТЗ, НомерСтроки, ПоВнутрПредставлению = 0)
   Перем нк, ИмяКолонки, ЗначениеКлюча, ЗначениеТЗ;
   
   Для нк = 1 По Ключ.РазмерСписка() Цикл
       ЗначениеКлюча = Ключ.ПолучитьЗначение(нк, ИмяКолонки);
       ЗначениеТЗ    = ТЗ.ПолучитьЗначение(НомерСтроки, ИмяКолонки);
       Если ТипЗначения(ЗначениеКлюча) > 3 Тогда
           ЗначениеКлюча = ЗначениеДляСравнения(ЗначениеКлюча, ПоВнутрПредставлению);
           ЗначениеТЗ    = ЗначениеДляСравнения(ЗначениеТЗ,    ПоВнутрПредставлению);
       КонецЕсли;
       Если ЗначениеКлюча > ЗначениеТЗ Тогда
           Возврат 1;
       ИначеЕсли ЗначениеКлюча < ЗначениеТЗ Тогда
           Возврат -1;
       КонецЕсли;
   КонецЦикла;
   
   Возврат 0;
КонецФункции

//Бинарный поиск по ключу. Возвращается номер первой или последней строки, совпадающей с ключом
//Таблица должна быть предварительно отсортирована (для этого предназначен метод СортироватьПоКлючу())
//Параметры:
//  - ТЗ - таблица значений, в которой нужно найти строку
//  - Ключ - список значений, по которым производится поиск. Текстовое представление значения д.б. именем колонки ТЗ.
//  - ПоВнутрПредставлению - если 1, то при сравнении используется внутреннее представление объекта.
//       Это нужно в тех случаях, когда есть разные объекты с одинаковым представлением (например,
//       разные контрагенты с одинаковым наименованием). Естественно, сортировать ТЗ также нужно по внутр.
//       представлениям (см. СортироватьПоКлючу()).
//  - НачСтрока, КонСтрока - если отличны от 0, то для поиска будет использован только указанный диапазон строк.
//       В процессе поиска эти значения меняются таким образом, что их можно затем использовать для
//       ускорения поиска второй границы. Например:
//       НачСтрока = 0; КонСтрока = 0;
//       ТЗ_НайтиПоКлючу2(ТЗ, Ключ, НачСтрока, КонСтрока, 0); //Находим первую запись
//       ТЗ_НайтиПоКлючу2(ТЗ, Ключ, НачСтрока, КонСтрока, 1); //Находим последнюю запись, но уже гораздо быстрее
//  - НайтиПоследнюю - 0 - будет найдена первая строка, совпадающая с ключом; 1 - последняя
Функция ТЗ_НайтиПоКлючу2(ТЗ, Ключ, ПоВнутрПредставлению = 0, НачСтрока=0, КонСтрока=0, ИскатьПоследнюю = 0) Экспорт
   Перем н1, н, н2, Рез;
   
   Если НачСтрока = 0 Тогда
       н1 = 1;
   Иначе
       н1 = НачСтрока;
   КонецЕсли;
   Если КонСтрока = 0 Тогда
       н2 = ТЗ.КоличествоСтрок();
   Иначе
       н2 = КонСтрока;
   КонецЕсли;
   Найдено = 0;
   
   Пока н1 < н2 Цикл
       н = Цел((н1+н2) / 2);
       Если ИскатьПоследнюю = 1 Тогда
           н = мин(н + 1, н2);
       КонецЕсли;
       
       Рез = СравнитьСКлючом(Ключ, ТЗ, н, ПоВнутрПредставлению);
       Если Рез = 0 Тогда
           Если ИскатьПоследнюю = 0 Тогда
               н2 = н;
           Иначе
               н1 = н
           КонецЕсли;
           Найдено = 1;
       ИначеЕсли Рез < 0 Тогда
           н2 = н - 1;
           КонСтрока = н2;
       Иначе
           н1 = н + 1;
           НачСтрока = н1;
       КонецЕсли;
   КонецЦикла;
   
   Если Найдено = 0 Тогда
       Если СравнитьСКлючом(Ключ, ТЗ, н1, ПоВнутрПредставлению) = 0 Тогда
           Найдено = 1;
       КонецЕсли;
   КонецЕсли;
   
   Если Найдено = 1 Тогда
       ТЗ.ПолучитьСтрокуПоНомеру(н1);
       Возврат н1;
   Иначе
       Возврат 0;
   КонецЕсли;
КонецФункции

//_____________________________________________________________________________
Процедура СортироватьПоКлючу(ТЗ, Ключ, ПоВнутрПредставлению = 0) Экспорт
   Перем нк, ИмяКолонки, СтрокаСортировки, Зпт;
   
   СтрокаСортировки = ""; Зпт = "";
   Для нк = 1 По Ключ.РазмерСписка() Цикл
       Ключ.ПолучитьЗначение(нк, ИмяКолонки);
       СтрокаСортировки = СтрокаСортировки + Зпт + ИмяКолонки;
       Зпт = ",";
   КонецЦикла;
   
   Если ПоВнутрПредставлению = 1 Тогда
       СтрокаСортировки = "*" + СтрЗаменить(СтрокаСортировки, ",", ",*");
   КонецЕсли;
   
   ТЗ.Сортировать(СтрокаСортировки, 1);
КонецПроцедуры
46 GenAcid
 
19.10.12
08:51
(41)

//Вариант 1
Для каждого СтрокаТЗ из ТЗ Цикл
 ...
КонецЦикла;

//Вариант 2
Запрос.Текст = "Выбрать ... Где ..."

//Вариант 3. Если очень хочется ПолучитьСтрокуПоНомеру()
Для инд = 1 по ТЗ.Количество() Цикл
 ТЗ.ПолучитьСтрокуПоНомеру(инд);
 ...
КонецЦикла;

Действительно "Иногда лучше жевать". И не мучай больше бедный стек вызовов, без особой нужды.
47 Stillcat
 
19.10.12
08:53
(41) Вы не правы, Ваш вариант медленнее!
Это что касается ПолучитьСтроку()и ПолучитьСтрокуПоНомеру()

Еще различия могут быть что сравнение по номенклатуре вынесено у Вас в отдельное условие и проверка напр. (ТЗ.ДатаПартии - ДатаПоступления) < 0) для большинства строк вообще не выполняется,
Но в моём варианте условия тоже можно легко разделить.
48 GenAcid
 
19.10.12
08:56
(47) Ах тыж 7ка)
49 Stillcat
 
19.10.12
08:58
Кто-нибудь знает, 1С проверяет сложные условия полностью или по сокращенной схеме?
50 ADirks
 
19.10.12
09:06
(49) полность.
51 0xFFFFFF
 
19.10.12
09:06
(49) 7.7 проверяет условие целиком, 8.х - по сокращенной.
Это один из пунктов, почему меня подташнивает от 7.7
52 kloptula
 
19.10.12
09:08
(47) Чего спорить-то? Изначально сделал по Вашему варианту. Меня не устроила скорость работы отчета. Поэтому и начал извращаться.
53 kloptula
 
19.10.12
09:11
(46) Такое ощущение, что для большинства присутствующих - рекурсия что-то злое и неправильное. Лучше 100500 строк дополнительного кода написать, чтобы потом никто не разобрался, как оно работает
54 kloptula
 
19.10.12
09:13
(46) можно было и запросом изъеб..ться, лучше уж "мучать  бедный стек вызовов", чем мучать бедный регистр остатков
55 Simod
 
19.10.12
09:33
(41) Поиск с использованием А НайтиЗначение() и ПолучитьСтрокуПоНомеру() может быть быстрее, когда надо проверить несколько строк. Если надо проверить несколько десятков или сотен строк, то медленнее.

Я так думаю, что там можно было все запросом получить и не изобретать велосипед с квадратными колесами..
56 Stillcat
 
19.10.12
09:33
(52) Глупости
57 ptiz
 
19.10.12
09:38
Ну и код.
Если к (36) добавить индексы на 3 поля и использовать НайтиСтроки, то будет летать с 1ой космической.
58 kloptula
 
19.10.12
09:40
(56) см. (55) как раз мой случай, проверка нескольких строк
59 kloptula
 
19.10.12
09:40
(55) Там в цикле проверка идет. Запрос в цикле не айс гонять
60 Simod
 
19.10.12
09:45
(59) "Там в цикле проверка идет. Запрос в цикле не айс гонять"

Твою ветку уже поместили сюда: OFF: ПятницО
Хочешь стать героем дня?