Как вырваться из вложенных циклов?


96

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

Есть ли другой способ разорвать все петли? (Пожалуйста, не используйте goto stmt.)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2

2
вы можете попробовать int i и int j до начала цикла, а затем при условии make их 1001 цикл не будет повторять следующий цикл.
Khurram Ijaz

Ответы:


43

Использование:

if (condition) {
    i = j = 1000;
    break;
}

49
Работает, но некрасиво и не в общем. Что, если кто-то изменит ограничение на 2000 (предположим, что код длиннее, поэтому вы не сразу заметите это)?
ugoren

1
@ugoren Тогда все так просто. что, если вы использовали const int count =1000 в глобальном файле Initialization. или как #defineмакрос.
Лаксит

4
Как указывает @ugoren, это не общее решение. Поскольку это первый ответ Google на этот вопрос, было бы неплохо, если бы было выбрано общее решение. Ну, люди все равно привыкли проверять №2.
BeeOnRope

1
Думаю, нужно только i = 1000?
Питер Ву

189

Нет, не портите удовольствие break. Это последнее оставшееся допустимое использование goto;)

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

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

Обобщенно - для выхода из вложенных циклов:

  1. использовать goto
  2. использовать флаги
  3. разложить циклы на отдельные вызовы функций

Не удержался и включил сюда xkcd :)

введите описание изображения здесь

источник

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


29
Гото примерно так же ясен, как и вы, да. Установка переменной выхода на 1000 еще более сложна.
Correnos

3
Я хотел бы добавить, что gotos не являются злом явно, их просто можно использовать во зло. Я считаю, что есть довольно много случаев, например, в этом, когда они являются лучшим решением. «Не использовать gotos» - хорошее начало, но я думаю, что следующий шаг в навыке позволит вам «Не использовать gotos дальнего действия».
Aatch

1
Я бы не согласился с этим: «Создание функции приводит к экспоненциальному увеличению / уменьшению указателя стека». Если есть локальная (статическая) функция, которая вызывается только в одной точке потока программы, любой полуприличный компилятор встроит ее, и результирующий код по существу такой же, как и с goto. Возможно, это самый простой вариант оптимизации для любого компилятора.
DrV

1
Рефакторинг - обычно самое чистое решение. Однако, если какие-либо переменные вне цикла изменяются во время внутреннего цикла, все усложняется. Одна из возможностей - передать переменную внутренней функции по ссылке (указателю), но это может сбить с толку оптимизацию компилятора и привести к ненужному дополнительному коду. Другая возможность - сделать такие переменные статическими на уровне модуля, но это тоже не очень красиво. К сожалению, в C отсутствуют вложенные функции, поскольку они решат эту проблему - если только вы не захотите использовать gcc, предоставляющий расширение.
DrV

1
+1. А статья Дональда Э. Кнута «Структурированное программирование с переходом к операторам» ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) - интересная статья, которая уравновешивает статью Дейкстры.
kmkaplan

40
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}

Решение по-прежнему увеличивает обе переменные на единицу при перерыве, что может вызвать проблемы
TheSola10

7
Можно установить stop = true; а потом "перерыв". Затем сразу после окончания внутреннего цикла for выполните команду if (stop) break;
Джефф Григг

34

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

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}

1
кажется лучшим решением для меня
Лука Стиб 03

20

Думаю gotoрешу проблему

for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
        if (condition) {
            goto end;
        }
    }
}

end:
stmt2 

@chikuba Я получил ответ от cprogramming.com/tutorial/goto.html, и ваш ответ не опубликован, когда я делаю то же самое, поэтому я не вижу вашего сообщения
Renjith KN

15

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

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

Если вы хотите, чтобы он был менее читабельным, вы можете присоединиться к логической оценке:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

В качестве окончательного способа вы можете аннулировать начальный цикл:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}


4

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

Если вы используете GCC, проверить эту библиотеку . Как и в PHP, breakможет принимать количество вложенных циклов, из которых вы хотите выйти. Можно написать примерно так:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}

И под капотом он действительно используетсяgoto :)
jacobq

@ iX3 Я могу использовать встроенный ассемблер и инструкцию jmp, если это поможет.
DaBler

@DaBler, я не знал, что вы являетесь автором этой библиотеки. Мой комментарий не был задуман как отзыв, а скорее отмечал, что эта библиотека использует тот же метод, что и принятый ответ . Надеюсь, ваш комментарий был задуман как шутка, потому что я думаю, что использование языковой функции (даже goto) намного предпочтительнее встроенного asm (зависит от машины, проще ошибиться, сложнее читать ...).
jacobq


3

Если вам нужны значения i и j, это должно работать, но с меньшей производительностью, чем другие

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}

Обратите внимание, что если условие зависит от, jто значение условия необходимо каким-то образом сохранить, чтобы это все еще работало.
SuperBiasedMan 03

1
Вы правы, но после перерыва значение j не меняется, как и значение условия.
Али Эрен Челик

Это неработающее решение и в целом недействительно. Либо J не определен вне его цикла или for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }будет всегда вырваться из внешнего цикла после первой итерации внутреннего цикла.
Чай Т. Рекс,


-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}

По сути, вы говорите, что он должен (1) сделать кучу дополнительных вызовов функций, а затем (2) вращаться в остальное время, когда conditionстановится ложным. Да, и второй цикл будет работать вечно, потому что он увеличивается, iа не j, упс ...
jacobq

-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

} while ((i < 1000) && !condition);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.