Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Проблема с логикой в цикле с выходом по условию


Автор: SABROG 30.7.2009, 21:24
Есть такой рабочий код:

Код

bool flag = false;
    do {
        while (!xml->atEnd()){
            token = xml->readNext();

            if (implementation()) {
                flag = true;
                break;
            }
        }
        if (flag || token == QXmlStreamReader::EndDocument) {
            break;
        }
    } while (!isCritical());


Выглядит он, мягко говоря, кривовато. По сути это основной цикл StAX парсера.

Проблема в следующем. С целью оптимизации создал 2 вложенных цикла. Раньше проверка isCritical() шла на каждом витке цикла. Профайлер четко мне сказал, что этот метод один из самых жрущих процессорное время. Метод atEnd() возвращает true в двух случаях: достиг конец документа (все ОК) и возникла ошибка. Ошибка может быть не критической, после которой возможно продолжение цикла, просто добавляется новая порция данных для парсинга. Т.е. теперь внутренний цикл выполняется без проверки на ошибку каждый раз. А если она возникает, то идет выход (atEnd) из внутреннего цикла во внешний. К сожалению в C++ нет метода типа ::break, чтобы выйти сразу из всех вложенных циклов. Пришлось вставить костыль в виде булевой переменной flag. По началу я впихнул проверку этой переменной наряду с isCritical таким образом:

Код

} while (!isCritical() || flag);


Проблема возникла в особенности языка C++. Если первое условие не выполняется, то второе условие не проверяется вообще. Пришлось проверку вынести  в if. Ума не приложу как в одну строчку написать такое условие:
- если flag не установлен (false), то выходим из всех циклов, при условии, что isCritical() вернет положительное значение (true), при том, что если flag все-таки установлен (true), то выходим из циклов не зависимо от того, что вернет isCritical().

(flag==true && isCritical()==true||false) - выход из циклов (метод isCritical() должен вызываться только один раз)
(flag==false && isCritical()==true) - выход из циклов (здесь метод isCritical() должен вызываться независимо от значения flag, т.к. происходящее внутри может на него повлиять при следующем витке, но как я говорил выше компилятор проверяет только первое условие, и если оно отрицательное, то второе условие не выполняется, что приводит к бесконечным итерациям.).

Итого проблемы 2:
- выход из двух вложенных циклов while без return или goto, отказавшись от проверки flag'a
- или написать условие в одну строку, которое могло бы заставить компилятор проверять второе условие независимо от результатов проверки первого.

Автор: azesmcar 30.7.2009, 21:37
Цитата(SABROG @  30.7.2009,  21:24 Найти цитируемый пост)

Ума не приложу как в одну строчку написать такое условие:
- если flag не установлен (false), то выходим из всех циклов, при условии, что isCritical() вернет положительное значение (true), при том, что если flag все-таки установлен (true), то выходим из циклов не зависимо от того, что вернет isCritical().

Код

if ( flag || (!flag && isCritical()) ) ...


остальное как-то запутанно написано, или мне сильно спать хочется. smile 

Автор: GoldFinch 30.7.2009, 21:44
для выхода из 2х циклов сразу используйте return

Автор: mes 30.7.2009, 22:07
если не сложно приведите первоначальный вариант парсера, мне кажется так будет легче помочь Вам правильно соптимизировать Ваш цикл.

Автор: ISergeyN 30.7.2009, 22:30
Цитата(SABROG @  30.7.2009,  21:24 Найти цитируемый пост)
 К сожалению в C++ нет метода типа ::break, чтобы выйти сразу из всех вложенных циклов.

Наверно один из случаев когда можно использовать goto  smile  

Автор: SABROG 30.7.2009, 22:52
azesmcar, спасибо. Окончательный вариант получился таким:

Код

} while (((!flag && !isCritical()) || !flag) && (token != QXmlStreamReader::EndDocument));


Цитата(GoldFinch @  30.7.2009,  21:44 Найти цитируемый пост)
для выхода из 2х циклов сразу используйте return 


Это мне тогда придется выносить цикл while в отдельный метод, т.к. после цикла идет еще код, а он не выполнится, если я выйду по return.

Цитата(mes @  30.7.2009,  22:07 Найти цитируемый пост)
если не сложно приведите первоначальный вариант парсера, мне кажется так будет легче помочь Вам правильно соптимизировать Ваш цикл.


Первоначального не существует как такового, по крайней мере для Qt, где всё на словах в документации, а примеры довольно примитивные.

Примеры на Java можно посмотреть тут http://www.cafeconleche.org/slides/sd2004west/stax/StAX.html , только вот они тоже очень уж примитивные. Как минимум там нет реализации того, что является главным достоинством StAX'a - поточности. А именно не реализован механизм догрузки данных с последующим продолжением парсинга. Именно на это нацелены эти 2 цикла. Когда данных не хватает (битый, частично загруженный xml) приходит ошибка. По сути ошибка не критичная и метод isCritical() проверяет характер ошибки и вызывает метод догрузки или ожидания новых данных. Если в процессе ожидания пользователь отменил дальнейший парсинг или пропала связь с сервером, то isCritical вернет true. Если новые данные были получены, то возвращается false и цикл продолжается дальше.

Цитата(ISergeyN @  30.7.2009,  22:30 Найти цитируемый пост)
Наверно один из случаев когда можно использовать goto

Хотелось бы услышать еще мнения ЗА goto от других людей.

Автор: mes 31.7.2009, 00:53
Цитата(SABROG @  30.7.2009,  21:52 Найти цитируемый пост)
Хотелось бы услышать еще мнения ЗА goto от других людей.


Цитата(SABROG @  30.7.2009,  21:52 Найти цитируемый пост)
    
Окончательный вариант получился таким:
Код

} while (((!flag && !isCritical()) || !flag) && (token != QXmlStreamReader::EndDocument));

насколько понял Вам нужно прекратить цикл когда сработает одно из 3х условий:
Код

while ((!isCritical() &&  !flag  && token != QXmlStreamReader::EndDocument);


Цитата(SABROG @  30.7.2009,  21:52 Найти цитируемый пост)
Это мне тогда придется выносить цикл while в отдельный метод, т.к. после цикла идет еще код, а он не выполнится, если я выйду по return.

ну так одна функция и не должна решать  больше одной проблемы  smile 

Цитата(SABROG @  30.7.2009,  21:52 Найти цитируемый пост)
Первоначального не существует как такового,

имелся ввиду парсер до начала Вашей оптимизации. smile

Цитата(SABROG @  30.7.2009,  21:52 Найти цитируемый пост)
Хотелось бы услышать еще мнения ЗА goto от других людей. 

отдайте предпочтение разбиению на функции - уверен, что не пожалеете. smile 



Автор: azesmcar 31.7.2009, 07:00
SABROG

По поводу goto http://forum.vingrad.ru/forum/topic-251118/0.html

Цитата(mes @  31.7.2009,  00:53 Найти цитируемый пост)
while ((!isCritical() &&  !flag  && token != QXmlStreamReader::EndDocument);

Я бы перенес !flag на первое место, !isCritical() самая медленная проверка, пусть в первую очередь проверяется !flag, хотя это может избавить только от одного вызова IsCritical(), но все равно.. Мелочь а приятно smile

Автор: SABROG 31.7.2009, 08:42
Да, я сам накосячил. Пробовал эти варианты, только у меня не было выхода по третьему условию (token != QXmlStreamReader::EndDocument). Весь мозг сломал почему не выходит из цикла smile

Цитата(azesmcar @  31.7.2009,  07:00 Найти цитируемый пост)
По поводу goto

Ты же вроде сам был против, поменял мнение? smile http://forum.vingrad.ru/index.php?showtopic=251118&view=findpost&p=1810869

Пока сделал отдельный метод. Может его сделать inline, чтобы особо не влияло на скорость парсинга или компилер сам позаботится об этом smile?

Автор: azesmcar 31.7.2009, 08:53
Цитата(SABROG @  31.7.2009,  08:42 Найти цитируемый пост)
Ты же вроде сам был против, поменял мнение? 

Где я это говорил? Я сказал
Цитата(azesmcar @  14.3.2009,  08:28 Найти цитируемый пост)

если безопаснее и изящнее - нет проблем. Но я лично не встречал случая где его нужно было бы использовать и даже без стиля я бы много много раз подумал перед тем как оставить код с гото smile

Я лично никогда его не использовал, но там есть то что ты просил

Цитата(SABROG @  30.7.2009,  22:52 Найти цитируемый пост)
Хотелось бы услышать еще мнения ЗА goto от других людей. 

а именно, много мнений smile

Добавлено через 1 минуту и 57 секунд
Цитата(SABROG @  31.7.2009,  08:42 Найти цитируемый пост)
Пока сделал отдельный метод. Может его сделать inline, чтобы особо не влияло на скорость парсинга или компилер сам позаботится об этом smile? 

А сейчас медленно работает? Если нет - то зачем об этом думать? Скорость устраивает - все отлично, продолжай работать. Думать будешь об этом тогда, когда работать будет медленее чем ты от него ожидаешь. А так просто - оптимизация ради оптимизации зло.

Автор: bsa 31.7.2009, 17:13
Лично я считаю выход из вложенного цикла одним из случаев, когда использовать goto целесообразно. Так как введение дополнительного флага это уже пессимизация.

Автор: mes 31.7.2009, 17:38
Цитата(bsa @  31.7.2009,  16:13 Найти цитируемый пост)
Лично я считаю выход из вложенного цикла одним из случаев, когда использовать goto целесообразно. Так как введение дополнительного флага это уже пессимизация. 

ага, особенно если флаг притянут к логике цикла за уши. smile

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

P.S. если в методе не используется this для успокоения души можете делать такой метод статическим. smile


Автор: SABROG 31.7.2009, 20:40
Цитата(azesmcar @  31.7.2009,  08:53 Найти цитируемый пост)
Где я это говорил?


Вот эта фраза. 

Цитата

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


Ну да ладно, я в любом случае последовал совету mes'a. Оценил также совет bsa и сделал вывод, что в принципе в такой ситуации можно воспользоваться goto, но предпочту этого не делать, чтобы не вырабатывать в себе плохую привычку ;)

Цитата(azesmcar @  31.7.2009,  08:53 Найти цитируемый пост)
А сейчас медленно работает? Если нет - то зачем об этом думать?


Работать будет в любом случае медленно. На php парсинг того объема xml данных, что через меня проходит каждый день занимает минимум 30-40 минут в день (сам процесс парсинга). Обычно в сумме это где-то 300-400Мб. Когда я пытался в последний раз парсить это дело на C++ через DOM, то на это уходило минут 5. Не критично, но ведь не быстро.

Думаю вопрос решен.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)