Неправильно ли использовать перерыв в цикле for?

Неправильно ли использовать инструкцию break внутри цикла for?

Скажем, я ищу значение в массиве. Сравните внутри цикла for и когда значение найдено, break;, чтобы выйти из цикла for.

Это плохая практика? Я видел используемую альтернативу: определите переменную vFound и установите ее в true, когда значение найдено, и проверьте vFound в условии оператора for. Но нужно ли создавать новую переменную только для этой цели?

Я спрашиваю в контексте обычного цикла C или С++.

P.S: Рекомендации по кодированию MISRA советуют не использовать перерыв.

Ответ 1

Здесь много ответов, но я еще не видел этого:

Большинство "опасностей", связанных с использованием break или continue в цикле for, отрицаются, если вы пишете аккуратные, легко читаемые циклы. Если тело вашего цикла охватывает несколько экранов и имеет несколько вложенных подблоков, да, вы можете легко забыть, что после разрыва какой-то код не будет выполнен. Если, однако, цикл является коротким и точным, цель оператора break должна быть очевидной.

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

Ответ 2

Нет, break - правильное решение.

Добавление булевой переменной делает код более трудным для чтения и добавляет потенциальный источник ошибок.

Ответ 3

В них можно найти всевозможные профессиональные коды с инструкциями break. Совершенно разумно использовать это, когда это необходимо. В вашем случае эта опция лучше, чем создание отдельной переменной только для выхода из цикла.

Ответ 4

Использование break, а также continue в цикле for отлично.

Это упрощает код и улучшает его читаемость.

Ответ 5

В отличие от плохой практики, Python (и другие языки?) Расширил структуру цикла Ta40, поэтому ее часть будет выполняться только в том случае, если цикл не break.

for n in range(5):
    for m in range(3):
        if m >= n:
            print('stop!')
            break
        print(m, end=' ')
    else:
        print('finished.')

Выход:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

Эквивалентный код без break и удобного else:

for n in range(5):
    aborted = False
    for m in range(3):
        if not aborted:
            if m >= n:
                print('stop!')
                aborted = True
            else:            
                print(m, end=' ')
    if not aborted:
        print('finished.')

Ответ 6

Общее правило: если следовать правилу, вам нужно сделать что-то более неудобное и трудное для чтения, а затем нарушить правило, а затем нарушить правило.

В случае петли, пока вы не найдете что-то, вы столкнетесь с проблемой отличия найденной или не найденной, когда вы выходите. То есть:

for (int x=0;x<fooCount;++x)
{
  Foo foo=getFooSomehow(x);
  if (foo.bar==42)
    break;
}
// So when we get here, did we find one, or did we fall out the bottom?

Итак, вы можете установить флаг или инициализировать значение "found" равным null. Но

Вот почему я вообще предпочитаю подталкивать свои поиски к функциям:

Foo findFoo(int wantBar)
{
  for (int x=0;x<fooCount;++x)
  {
    Foo foo=getFooSomehow(x);
    if (foo.bar==wantBar)
      return foo;
  }
  // Not found
  return null;
}

Это также помогает разблокировать код. В основной строке "find" становится единственным выражением, и когда условия сложны, они записываются только один раз.

Ответ 7

Нет ничего плохого в использовании оператора break, но вложенные циклы могут привести к путанице. Для улучшения читабельности многие языки (по крайней мере Java поддерживает) поддерживают разбиение на метки, что значительно улучшит читабельность.

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};

// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {

    // label for j loop
    jLoop: for (int j = 0; j < jArray.length; j++) {

        if(iArray[i] < jArray[j]){
            // break i and j loops
            break iLoop;
        } else if (iArray[i] > jArray[j]){  
            // breaks only j loop
            break jLoop;
        } else {
            // unclear which loop is ending
            // (breaks only the j loop)
            break;
        }
    }
}

Я скажу, что операторы break (и return) часто увеличивают цикломатическую сложность, что затрудняет доказательство того, что код работает правильно во всех случаях.

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

Ответ 8

Это зависит от языка. Хотя здесь вы можете проверить логическую переменную:

for (int i = 0; i < 100 && stayInLoop; i++) { ... }

это невозможно сделать при использовании массива:

for element in bigList: ...

В любом случае break сделает оба кода более читабельными.

Ответ 9

break - вполне приемлемый оператор для использования (так что продолжить, кстати). Все о читаемости кода - если у вас нет слишком сложных циклов и т.д., Это нормально.

Не похоже, что они были той же лигой, что и goto.:)

Ответ 10

В вашем примере вы не знаете количество итераций цикла для. Почему бы не использовать вместо while цикл, который позволяет в начале числа индексировать число итераций?

Следовательно, нет необходимости использовать break statemement в целом, поскольку цикл может быть лучше указан как цикл while.

Ответ 11

Я согласен с другими, которые рекомендуют использовать break. Очевидный косвенный вопрос заключается в том, почему кто-либо порекомендовал иначе? Ну... когда вы используете break, вы пропускаете остальную часть кода в блоке и оставшиеся итерации. Иногда это вызывает ошибки, например:

  • ресурс, полученный в верхней части блока, может быть выпущен снизу (это справедливо даже для блоков внутри циклов for), но этот шаг освобождения может быть случайно пропущен, когда "преждевременный" выход вызванный оператором break (в "современном" С++, "RAII" используется для надежного и безопасного способа: в принципе, объектные деструкторы надежно освобождают ресурсы независимо от того, как открывается область видимости)

  • кто-то может изменить условный тест в инструкции for, не заметив, что существуют другие делозаключенные условия выхода

  • ndim отвечает, что некоторые люди могут избежать break, чтобы поддерживать относительно непротиворечивое время выполнения цикла, но вы сравнивали break с использованием контрольной переменной с булевым ранним выходом, где это не выполняется

Время от времени люди, наблюдающие за такими ошибками, понимают, что их можно предотвратить/смягчить с помощью этого правила "без перерывов"... действительно, существует целая взаимосвязанная стратегия для "безопасного" программирования, называемая "структурированное программирование", где каждая функция предполагается, что у него тоже есть одна точка входа и выхода (то есть нет goto, без раннего возвращения). Это может устранить некоторые ошибки, но, несомненно, представляет других. Почему они это делают?

  • у них есть структура разработки, которая поощряет определенный стиль программирования/кода, и у них есть статистические данные о том, что это дает чистую выгоду в этой ограниченной структуре или
  • на них повлияли руководящие принципы программирования или опыт в рамках такой структуры или
  • они просто диктаторские идиоты, или
  • любая из вышеупомянутых + историческая инертность (применительно к тому, что обоснования более применимы к C, чем современный С++).

Ответ 12

Совершенно верно использовать break - как указывали другие, это нигде в той же лиге, что и goto.

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

Ответ 13

Во встроенном мире существует много кода, который использует следующую конструкцию:

    while(1)
    { 
         if (RCIF)
           gx();
         if (command_received == command_we_are_waiting_on)
           break;
         else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
           return ERROR;
         num_attempts++;
    }
    if (call_some_bool_returning_function())
      return TRUE;
    else
      return FALSE;

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

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

Ответ 14

Я не вижу причин, почему было бы плохой практикой, ПРЕДОСТАВЛЕННОЙ, что вы хотите завершить обработку STOP в этот момент.

Ответ 15

Я сделал некоторый анализ кода, в котором я сейчас работаю (40 000 строк JavaScript).

Я нашел только 22 break оператора:

  • 19 использовались внутри операторов switch (у нас всего три оператора switch в целом!).
  • 2 использовались внутри циклов for - код, который я сразу же классифицировал как подлежащий рефакторингу на отдельные функции и заменялся оператором return.
  • Что касается финального цикла break внутри цикла while... я запустил git blame, чтобы узнать, кто написал эту хрень!

Итак, согласно моей статистике: Если break используется вне switch, это запах кода.

Я также искал инструкции continue. Не найдено.

Ответ 16

Зависит от вашего прецедента. Существуют приложения, в которых время выполнения цикла for должно быть постоянным (например, для удовлетворения некоторых ограничений по времени или для скрыть свои внутренние данные от атак с временной привязкой).

В этих случаях даже будет смысл устанавливать флаг и проверять только значение флага ПОСЛЕ того, что все итерации цикла for действительно запущены. Конечно, все итерации цикла for должны запускать код, который по-прежнему занимает примерно одно и то же время.

Если вам не интересно время выполнения... используйте break; и continue;, чтобы сделать код более удобным для чтения.

Ответ 17

В MISRA 98 правил, которые используются в моей компании в C dev, оператор break не используется...

Изменить: В MISRA '04 разрешен разрыв в

Ответ 18

Конечно, break; - это решение для остановки цикла for или foreach. Я использовал его в php в foreach и для цикла и нашел работу.

Ответ 19

Я не согласен!

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

Я думаю, что имеет смысл проверять ваши проверки в верхней части цикла for

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

или если вам нужно сначала обработать строку

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

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

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

Или, если ваше состояние затруднено, вы также можете переместить этот код!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

"Профессиональный код", который пронизан перерывами, для меня не очень похож на профессиональный код. Это звучит как ленивое кодирование;)