Як вирватися з вкладених циклів?


96

Якщо я використовую a 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 до того, як цикл почнеться, і тоді, за умови, якщо вони 1001, цикл не буде повторювати наступний.
Хурам Іджаз

Відповіді:


43

Використання:

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

49
Працює, але негарно і не загально. Що робити, якщо хтось змінить ліміт на 2000 (припустимо, код довший, тож ви не відразу його помічаєте)?
угорен

1
@ugoren Це теж так просто. що, якби ви використовували a const int count =1000 , у глобальній ініціалізації. або як #defineмакрос.
Лаксіт,

4
Як зазначає @ugoren, це не загальне рішення. Оскільки це перше звернення Google до цього питання, було б непогано, якби було обрано загальне рішення. Добре, люди звикли перевіряти номер 2 у будь-якому випадку.
BeeOnRope

1
Мені здається, потрібно лише i = 1000?
Пітер Ву

189

Ні, не псуйте задоволення за допомогою break. Це останнє дійсне використання goto;)

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

Іншим підходом до виходу з вкладеного циклу є виділення обох циклів в окрему функцію та повернення з цієї функції, коли ви хочете вийти.

Підсумовано - щоб вирвати вкладені цикли:

  1. використання goto
  2. використовувати прапори
  3. розкласти цикли на окремі виклики функцій

Не втримався, включивши сюди xkcd :)

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

джерело

Гото вважаються шкідливими, але, як багато людей у ​​коментарях вважають, що це не потрібно. Якщо використовувати розумно, це може бути чудовим інструментом. Все, що використовується в міру, приносить задоволення.


29
Goto приблизно такий же зрозумілий, як ти сюди потрапиш, так. Встановлення змінної виходу на 1000 ще важче.
Коренос

3
Я хотів би додати, що гото не є явно злим, їх просто можна використовувати для зла. Я виявляю, що існує досить багато випадків, наприклад цей, де вони є найкращим рішенням. "Не використовувати гото" - це хороший початок, але я думаю, що наступний крок у навичках дозволяє вам "Не використовувати гото з великої дальності".
Aatch

1
Я хотів би не погодитися з цим: "Створення функції призводить до експоненціальної кількості додавання / віднімання покажчика стека". Якщо є локальна (статична) функція, яка викликається лише в одній точці потоку програми, будь-який напівпристойний компілятор вбудує її, і отриманий код по суті такий самий, як і у goto. Це, мабуть, найпростіший випадок оптимізації для будь-якого компілятора.
DrV

1
Рефакторинг, як правило, є найчистішим рішенням. Однак, якщо будь-які змінні поза циклом змінюються під час внутрішнього циклу, все ускладнюється. Однією з можливостей є передача змінної внутрішній функції за допомогою посилання (покажчика), але це може заплутати оптимізацію компілятора і створити непотрібний додатковий код. Інша можливість - зробити такі змінні статичними на рівні модуля, але це теж не дуже красиво. На жаль, у C відсутні відсутні вкладені функції, оскільки вони могли б вирішити цю проблему - якщо ви не готові прив'язати себе до використання gcc, який надає розширення.
DrV

1
+1. А Структуроване програмування Дональда Е. Кнута з переходом до Statements ( 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;" а потім "зламати;". Потім, відразу після закінчення внутрішньої сторони "для" циклу, зробіть ", якщо (зупинка) перерва;".
Джефф Грігг,

34

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

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

1
здається найкращим рішенням для мене
Лука Стіб

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, і ваша відповідь не розміщується, коли я роблю те саме, ось чому я не бачу вашого повідомлення
Ренджіт К.Н.,

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того, значення цієї умови потрібно певним чином зберігати, щоб це все ще працювало.
SuperBiased, людина

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, що
лежить

-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.