Страницы: 1 2 3 4 5 След.
RSS
Циклы и метки. В каких случаях, что использовать будет эффективнее
 
Доброго времени суток, Планетяне!

Тема создана из масштабного оффтопа в этой теме. Возникла необходимость расставить все точки над "ё", как говорится.

Итак, предлагаю следующие критерии определения эффективности: 1. скорость, 2. стабильность, 3. понятность для НЕавтора кода. Можно ещё добавить подкритерии "универсальность" и "размер кода". Всякие "ментальные" понятия типа "так не принято" предлагаю не учитывать.

Начну с меток, т.к. они вызывают очень спорные мнения. Я их всегда использую для обхода непредвиденных ошибок и не представляю, чем их можно было бы заменить (разве что писать On Error Resume Next и проверять в каждой строке переменную ошибки If Err>0 Then.
Кроме того, я использую метки вместо многоуровневых If - Then — для меня так код намного понятнее и меньше.

Вот, собственно, пример кода с метками. Тот самый, о котором я вчера писал в оффтопе. Что и почему вы бы заменили в нём?
КОД
Вспомогательная функция объединения диапазонов


Ссылки:
О правилах написания кода., системы отступов и т.п.
О вредном GoTo
Изменено: Jack Famous - 04.07.2018 11:09:53
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
С ходу, сделал бы так
Код
If WF_rng.Areas.Count <> 1 Then Exit Function

вместо
Код
If WF_rng.Areas.Count <> 1 Then GoTo er
er: Set PRDX_FindCellsInRange = Nothing
"Все гениальное просто, а все простое гениально!!!"
 
Как по мне, то в данном случае GoTo нарушают структуру и мешают. Чем хуже If? (Not поставил, чтобы не переставлять блоки строк):
Код
If Not WF_Part Then
'========== Одно значение и полное совпадение
    For WF_i = 1 To UBound(WF_arrRng, 1)
        For WF_j = 1 To UBound(WF_arrRng, 2)
            If WF_arrRng(WF_i, WF_j) = WF_Val Then Set tmpRng = PRDX_RangeUnion(tmpRng, WF_rng(WF_i, WF_j))
        Next WF_j
    Next WF_i
'========== Одно значение и частичное совпадение
Else
    For WF_i = 1 To UBound(WF_arrRng, 1)
        For WF_j = 1 To UBound(WF_arrRng, 2)
            If InStr(WF_arrRng(WF_i, WF_j), WF_Val) > 0 Then Set tmpRng = PRDX_RangeUnion(tmpRng, WF_rng(WF_i, WF_j))
        Next WF_j
    Next WF_i
End If
 
Nordheim, в данном случае совершенно согласен, но, обычно у меня в начале отключается всё (.ScreenUpdating, например), а в конце всё включается (по метке fin). Как, например, в процедуре ниже.

vikttur, опять же — да, НО… Часто уровней вложенности намного больше и получается "поваленное дерево" кода, уходящее далеко вправо (из-за отступов для удобочитаемости)  :D а с метками всё проще — если не то-то, то идём сразу туда-то  :) вкусовщина, наверное?))
Процедура с метками
Вспомогательные функции
Запросы сумм с повтором, в случае ошибки
Изменено: Jack Famous - 04.07.2018 12:01:01
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
с метками всё проще
не помню у кого читал то ли у Лукина, то ли у Уокенбаха, но там написано совершенно противоположное, что с метками намного все сложней и можно запутаться, даже зациклить выполнение кода, сам того не подозревая, вот поэтому я вообще не использую метки.
"Все гениальное просто, а все простое гениально!!!"
 
Nordheim, может быть просто таких объёмов не было у меня))) как по мне, метки позволяют пропускать/перепрыгивать огромные участки кода, сразу перемещаясь в нужное место. Фактически, получается точно. как на блок-схеме. Осмысленные названия меток обязательны  ;)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
1. Jack Famous, полные листинги кодов - зачем?
2. Метки - большое зло,  родственник объединения ячеек или ДВССЫЛ :)
 
Jack Famous, просто Вы привыкли к, так называемому, выносному методу обработки ошибок, ну  и это влияет на остальные .  Не скажу что часто сажусь за VBA(VBS) но метки не использовал уже много лет.
Вот чем хуже?
Код
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
PRDX_HaveFull = False
    For Each WF_cl In WF_rng
        If Len(WF_cl.Value) > 0 Then
            PRDX_HaveFull = True
            Exit For
        End If
    Next WF_cl
End Function

или
Код
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
    For Each WF_cl In WF_rng
        If Len(WF_cl.Value) > 0 Then
            PRDX_HaveFull = True
            Exit For
        End If
    Next WF_cl
PRDX_HaveFull = (PRDX_HaveFull = True)
End Function
Изменено: БМВ - 04.07.2018 12:14:25
По вопросам из тем форума, личку не читаю.
 
vikttur, 1. в первом спойлере повсюду метки, второй - просто функции для работы первого (вдруг кто-то из новичков заглянет и себе возьмёт, да и просто для примера), 3ий код сокращён (убрано ненужное)  :)
2. ну не знаааааюууууу. Сравнивать метки с объединёнными ячейками - это мне вот щас обидно было  :D

БМВ, согласен — в PRDX_HaveHide без меток сделан такой же цикл. Я вообще эти функции давно не переписывал - надо на массивы перевести)) прикрепил, чтобы первый код работал (2 из 3х в нём есть).

В первом спойлере без меток получится "поваленное дерево", в 3ем не знаю как "возврат" при ошибке ввода сделать без меток. Да и остаётся вопрос обхода непредусмотренных ошибок. А даже, если все ошибки известны: есть ведь разница — при каждой не пройденной проверке писать
Код
MsgBox "Сообщите разработчику…", vbCritical, "НЕПРЕДВИДЕННАЯ ОШИБКА"
MsgBox "Отмена выполнения…", vbInformation, "ВЫХОД"
On Error GoTo 0: Application.ScreenUpdating = 1: Application.DisplayAlerts = 1
или отправлять по соответствующим меткам в конец, где написано один раз?
Код
GoTo fin
er: MsgBox "Сообщите разработчику…", vbCritical, "НЕПРЕДВИДЕННАЯ ОШИБКА"
ex: MsgBox "Отмена выполнения…", vbInformation, "ВЫХОД"
fin: On Error GoTo 0: Application.ScreenUpdating = 1: Application.DisplayAlerts = 1
Изменено: Jack Famous - 04.07.2018 12:31:49
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
vikttur написал:
2. Метки - большое зло,  родственник объединения ячеек или ДВССЫЛ
Полностью согласен, не так давно программирую и по началу пару раз споткнулся при применении меток, и вот уже 1,5 года моего скромного стажа в VBA я не использую метки  :D
"Все гениальное просто, а все простое гениально!!!"
 
Jack Famous, разместите ссылку в теме "о вредном GoTo". Фактически те же обсуждения.

Цитата
второй - просто функции для работы первого (вдруг кто-то из новичков заглянет и себе возьмёт, да и просто для примера)
Давайте во всех темах попутно выкладывать все, что может пригодиться...
Мое мнение - много кода отвлекает от основного вопроса, тему становится неинтересно читать. Вопрос по меткам - так и коды сократить, чтобы отразить суть вопроса.

Цитата
vikttur написал: это мне вот щас обидно было
А когда пишем любителям объединения о неправильных их действиях - им не обидно? Украшал-прихорашивал - а они раскритиковали... И невдомек им, что делают не всегда правильно.
 
Цитата
Jack Famous написал:
PRDX_HaveHide без меток сделан такой же цикл.
сделан то сделан, только вот Then PRDX_HaveHide = True: Exit Function - осторожнее с этим не увлекайтесь. Лучше лишняя строка чем гемор из-за нерабочего кода. Не стоит после THEN использовать разделитель операторов.
Я там тоже не совсем прав , по умолчанию уже функция вернет FALSE, так что просто
Код
Public Function PRDX_HaveFull(WF_rng As Range) As Boolean
Dim WF_cl As Range
    For Each WF_cl In WF_rng
        If Len(WF_cl.Value) > 0 Then
            PRDX_HaveFull = True
            Exit For
        End If
    Next
End Function


Цитата
Jack Famous написал:
в 3ем не знаю как "возврат" при ошибке ввода сделать без меток.
Алексей, вот читать код вроде умею, но сейчас читаю  - как рецепт врача, вроде и написано но что ??? . :-)
er: MsgBox "Сообщите разработчику…", vbCritical, "НЕПРЕДВИДЕННАЯ ОШИБКА"
ex: MsgBox "Отмена выполнения…", vbInformation, "ВЫХОД"

то есть при определенных ошибках будет два сообщения? ну как то это  …….
Изменено: БМВ - 04.07.2018 12:54:36
По вопросам из тем форума, личку не читаю.
 
Nordheim, главное, что всё работает!  :) Как вы обходите ошибки? Как сделаете возврат к вводу при ошибке?

vikttur, разместил.
Цитата
vikttur написал: Вопрос по меткам
это только для начала))) оффтопить начали по поводу того, что я ещё и Do-Loop не использую, поэтому далее хотел здесь разобрать примеры, где Do-Loop наиболее эффективен по сравнению с For-Next и If-Then
Цитата
БМВ написал: Лучше лишняя строка чем гемор из-за нерабочего кода
я тестирую)) а вообще сталкивался пару раз с проблемами, но закономерности не выявил и пока не могу отказать себе в удовольствии писать одну строку вместо минимум 4х (в классическом виде)  :)
Цитата
БМВ написал: при определенных ошибках будет два сообщения?
совершенно верно. Так и задумано. Если возникает непредвиденная ошибка, то пользователю сначала сообщается об этом, а потом о том, что вся процедура отменяется. Если же осуществлён переход по "ex", то это просто отмена процедуры (часто самим пользователем, например, через кнопку "Отмена" в MsgBox или InputBox)
Изменено: Jack Famous - 04.07.2018 13:04:38
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Начинал программировать я в 8м классе с рисования блок-схем. Это на долго и глубоко увлекшее меня занятие выработало целую систему записи для сложных алгоритмов. Я одно время очень заморачивался этим и  по букварям стремился проектировать код без лишних меток и goto.
Но есть совершенно банальные конструкции, даже из четырех блоков алгоритма, которые не разрешимы без goto либо без повторения кода одного из блоков. Поэтому совсем избегать goto порою эквивалентно раздуванию кода и я не пытаюсь любой ценою их избегать. Однако изначально заложенные в мой мозг концептуальные подходы, среди которых было и построение общей архитектуры с минимумом перескоков между частями кода, на всю жизнь избавили написанные мною коды от излишних меток и goto.
 
Цитата
Neufazendnik написал: на всю жизнь избавили написанные мною коды от излишних меток и goto.
В яблочко. Вредно именно излишество, а не сам GoTo.

Цитата
БМВ написал:  осторожнее... не увлекайтесь... Не стоит после THEN использовать разделитель операторов.
Помедленнее, пожалуйста...
Я иногда использую. То, что " осторожнее" и нужно  смотреть, не вызовет ли такой прыжок нежелательных последствий, конечно, нужно. Но, может, еще какие предостережения?

Не так понял. Вернее, написано не так. Двоеточие - разделитель строк, а не операторов. Об этом же писалось? А я предложение понял как предостережение от выхода из-под If, минуя End  If
Да, все, что в одной строке после Then, будет исполнено только при выполнении условия перед Then. Поначалу на ти грабли наступал.
 
Цитата
Jack Famous написал:
Так и задумано
ну я б это  конечно одним сообщением делал.

OFF
Не то чтоб в защиту Jack Famous,  но просто скажу. Он пытается "впитать" всю информацию, которую можно взять и поднимает темы которые позволяют ему развивать свои навыки. Такое стремление заслуживает уважения, несмотря на все ошибки и иные подходы.
Изменено: БМВ - 04.07.2018 13:13:35
По вопросам из тем форума, личку не читаю.
 
По организации циклов я предпочитаю Do while всем остальным. Он мне кажется почему-то более маневренным и прозрачным. В нем нет скрытного механизма приращения счетчика и ограничений на использование счетчика после покидания тела цикла. Потому больше простор для манипуляций его значением и к тому же мне кажется, что исполняться он будет быстрее. Кроме того, мне нарвится, что в Do циклах проверочное условие можно располагать как до так после тела цикла. В общем Do нахожу более предпочтительным, нежели for и чаще всего организую через Do то, что можно было бы писать и через For за редким исключением.
Изменено: Neufazendnik - 04.07.2018 13:10:14
 
Цитата
БМВ написал:
одним сообщением делал
не получится. Одно сообщение говорит И об ошибке, И о выходе, а выход у меня ещё отдельно используется, так что так компактнее и логичнее (наверное).
Код
'примеры использования ТОЛЬКО выхода
If Selection.Cells.Count > 1000000 Then MsgBox "Не более 1 млн ячеек!", vbCritical, "RANGE ERROR": GoTo ex
'или
On Error GoTo ex
txt = Application.InputBox("Пограничные символы:", "Введите текст", " ", Type:=2)
If Len(txt) < 1 Or txt = "False" Then GoTo ex
Цитата
БМВ написал:
Не то чтоб в защиту  Jack Famous ,  но просто скажу
спасибо большое — от ВАС это очень приятно слышать, Михаил Васильевич  :oops:  :)

Neufazendnik, наверное, у меня что-то похожее, но обратное — к For…Next привык  :)
есть ли у вас пример (не просто код, а именно бытовая задача), в которой, на ваш взгляд, уверенно лидирует ваш метод?
Изменено: Jack Famous - 04.07.2018 13:12:49
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
а выход у меня ещё отдельно используется,
У меня та же ситуация. В этом году я пришел к правилу, что выходы лучше проектировать не множественные в отдельных частях кода, а по метке сводить все в одну строку, в которой и Screenupdating и Enableevents и StatusBar и on error возвращаются в нужное состояние. Т.е разумно все-таки выходить из подпрограммы в одной двух (в случае да\нет) точках, а не делать множество Exit Sub
 
Об операторах циклов.
Если логика позволяет  - For Each.
For - когда счетчик и нужно проходить весь диапазон без условий выхода из  цикла.
Do работает быстрее и при условиях правильнее применять именно этот оператор, а не For. Но часто не следую этому совету  - стопор в мозгу :)  Нужно искоренить.
 
Цитата
Neufazendnik написал:
разумно все-таки выходить из подпрограммы в одной двух (в случае да\нет) точках, а не делать множество Exit Sub
именно!  :D
То же самое у меня и с обобщающими сообщениями типа "Непредвиденная ошибка" и "Отмена выполнения"
Изменено: Jack Famous - 04.07.2018 13:18:03
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
vikttur написал:
стопор в мозгу
у каждого из нас есть — надо бороть))
Цитата
vikttur написал:
For - когда счетчик и нужно проходить весь диапазон без условий выхода из  цикла
правильно я понимаю, что Do может мне помочь обойтись без метки nx_bef в данном участке кода? (понимаю, что можно обойтись обратной проверкой во второй строке, но это возвращает к "поваленному дереву")
Код
For Each cl In Selection
        If (Len(cl) < 2) Or (Not cl.Value Like "*" & txt & "*") Then GoTo nx_bef
        pos1 = InStr(cl.Value, txt): If (pos1 = 1 Or pos1 = Len(cl.Value)) And choose2 = 7 Then GoTo nx_bef
        pos2 = pos1 + Len(txt) - 1
            If choose2 = 6 Then
                cl.Value = Mid$(cl.Value, pos2 + 1)
                cl.Value = Application.WorksheetFunction.Trim(cl.Value)
            Else
                cl.Value = Mid$(cl.Value, pos1)
                cl.Value = Application.WorksheetFunction.Trim(cl.Value)
            End If
nx_bef:
    Next cl
UPD: блин - у меня же не выход из цикла, а следующая ячейка
Изменено: Jack Famous - 04.07.2018 13:24:32
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
То же самое у меня и с обобщающими сообщениями типа "Непредвиденная ошибка" и "Отмена выполнения"
И у меня нечто подобное реализовано следующим образом:
Я делаю метку с msgbox "А" или inputbox "A" и вспомогательными манипуляциями, заканчивающуюся по resume next
А в эту метку попадаю не по goto, а записываю в "А" текстовое сообщение для вывода и далее генерирую икслючение, которое автоматом будет обработано по данной метке. Выведет сообщение с тестом "А", отменит тот же screenupdating или enableevents, чего-нибудь спросит у пользователя и отправит код на продолжение. Компакнее выходит, чем по 10 раз один и тот же код, выводящий разные сообщения, строчить.
 
Neufazendnik, я бы лучше код глянул, если можно) так что-то не соображу. Вы имеете ввиду это?
Код
Dim txt$
If 1 Then txt="один": Goto lblMsg
If 2 Then txt="два": Goto lblMsg
If 2 Then txt="три": Goto lblMsg
lblMsg: MsgBox txt
тут, конечно, Select Case лучше, но смысл тот же…
Изменено: Jack Famous - 04.07.2018 13:28:50
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
vikttur написал:
Если логика позволяет  - For Each.
Да, это шикарная штучка.
 
Цитата
Jack Famous написал:
Nordheim , главное, что всё работает!   Как вы обходите ошибки? Как сделаете возврат к вводу при ошибке?
Для этого есть цикл Do.....Loop, но честно говоря не приходилось сталкиваться с тем, что бы делать возврат при ошибке, к чему ведь ошибка не исчезла и в итоге мы получаем бесконечный цикл.
"Все гениальное просто, а все простое гениально!!!"
 
Цитата
Nordheim написал:
не приходилось сталкиваться с тем, что бы делать возврат при ошибке
вот, что я имею ввиду
Код
repNR:
MsgBox "Недопустимое значение!", vbCritical, "ОШИБКА ВВОДА"
On Error GoTo repNR
sumNR = Application.InputBox("Введите сумму Накладных Расходов по данной ведомости:", "Шаг №1", "", Type:=2)
If sumNR = "False" Then GoTo ex
If InStr(sumNR, ".") > 0 Or InStr(sumNR, "-") > 0 Then GoTo repNR
On Error Resume Next
If --sumNR <= 0 Or --sumNR <> --sumNR Or --sumNR <> Application.WorksheetFunction.Round(--sumNR, 2) Then GoTo repNR
If Err <> 0 Then GoTo repNR
sumNR = --sumNR
цикла здесь не будет — выход по штатной кнопке "Отмена" или крестику
Изменено: Jack Famous - 04.07.2018 13:31:59
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Nordheim написал:
Как вы обходите ошибки? Как сделаете возврат к вводу при ошибке?
A on error goto в комплекте с resume ... - Не вариант?
 
Цитата
Jack Famous написал: я бы лучше код глянул,
Код
        If sErr <> "" Then 
            Beep
            With ufQuote: .Show: .lbText.Caption = sErr: End With ' сообщение об ошибке
            Application.Wait Time:=Now + TimeValue("0:00:04") ' задержка
            Unload ufQuote
        End If

ufQuote - форма пльзователя с одним Label (lbText), в который выводится текст sErr. 4 секунды - и форма закрывается.

Вот уж действительно - тема обо всем )
 
vikttur, спасибо. Я бы просто описание ошибки в MsgBox передавал (пользователям это точно не нужно, так что, видимо, для отладки). Оно, конечно, без задержки будет, но вполне норм)) вкусовщина
Цитата
vikttur написал:
Вот уж действительно - тема обо всем )
оффтопим на здоровье - круто же  :D
Изменено: Jack Famous - 04.07.2018 13:44:08
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: 1 2 3 4 5 След.
Наверх