Як я можу вирватися з двох вкладених циклів для цілей у Objective-C?


74

У мене є два цикли for, вкладені таким чином:

for(...) {
    for(...) {

    }
}

Я знаю, що є breakзаява. Але мене бентежить, якщо він розриває обидві петлі або лише ту, в якій він був викликаний? Мені потрібно зламати обидва, як тільки я побачу, що немає сенсу повторювати більше разів.

Відповіді:


94

break виривається з одного циклу, але ви можете додати перевірку до зовнішнього циклу, який розривається, коли внутрішній обривається.

bool dobreak = false;
for ( ..; !dobreak && ..; .. ) {
   for ( ... ) {
      if (...) {
         dobreak = true;
         break;
      }
   }
}

56
ІМХО, використання goto набагато чистіше.
sigjuice

4
!dobreakзнаходиться не в тому місці; він повинен йти в умовному (для другої частини), а не кроку приросту (для третьої частини); Я б також використав !dobreak && .., щоб інші умови не потребували оцінки при порушенні. Я погоджуюсь із sigjuice: gotoце не зло при правильному використанні, і це випадок, коли goto робить кращий код.
ефемієнт

2
Це все одно буде виконувати можливий код у зовнішньому циклі, який буде після внутрішнього циклу
Martijn

@Martijn - цю проблему можна вирішити шляхом заміни breakз continueзаявою. Тим не менше, це просто більш неочевидні речі, про які слід турбуватися.
Ori Pessach

107

Якщо використання goto спрощує код, тоді це було б доречно.

for (;;) 
{
    for (;;) 
    {
        break; /* breaks inner loop */
    } 
    for (;;) 
    {
        goto outer; /* breaks outer loop */
    }
} 
outer:;

4
Щоб for (;;) {for (;;) {break; /* breaks inner loop */} for (;;) {goto outer; /* breaks outer loop */}} outer:;
продовжити

7
(;;) виглядає як маленьке обличчя.
zakdances

14

Оператор breakлише витягує вас із внутрішнього циклу. Якщо вам не потрібні додаткові накладні витрати в коді, пам'яті та продуктивності виділеної змінної стану, я рекомендую переробити код у власну функцію або метод і використовувати returnдля виходу з усіх циклів:

void do_lots_of_work(void)
{
  int i, j;

  for(i=0; i<10 ; i++)
  {
    for(j=0;j< 10; j++)
    {
     ..
     ..
     if(disaster_struck())
      return; /* Gets us out of the loops, and the function too. */
    }
  }
}

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

6
Гірше, на мій погляд, це втрата здатності розуміти алгоритм, читаючи його в одному місці. Все це, щоб уникнути goto?
Орі Пессах,

Я також стикався з цією проблемою перерви / продовження, іноді бажаючи продовжити цикл на 2-3 рівні вище. Найкраще зробити IMHO - це переформатувати вкладені цикли в метод, щоб цей метод міг повернути, чи слід продовжувати батьківський цикл.
Джейсон

що, якщо користувач хоче вийти лише з циклу, а не з функції?
Akshay J

На мою думку, це найкраще рішення. Це однозначно, що відбувається, його легко контролювати, його легко викликати, він не розподіляє та не оцінює непотрібні логічні значення, і він не використовує небезпечні старі практики, подібні goto. Проблеми щодо продуктивності також не повинні бути помітними у будь-якому реальному використанні.
Ben Leggiero,

9

Крім вже згаданої змінної прапор або Гото ви могли б кинути виняток Objective-C:

@try {
  for() {
    for() {
       @throw ...
    }
  }
}
@catch{
  ...
}

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

4
@Ori Pessach Ну Винятками є сучасні OO-версії goto ;-)
lothar

1
Вони принаймні добре прибирають стек.
Орі Пессач,

2
З мого досвіду, винятки дуже повільні і не повинні використовуватися для обробки потоку виконання.
Наталі Адамс,

3
Проведіть дослідження щодо винятків у ObjC. У документах Apple вони конкретно відзначають, що показник продуктивності є великим, і вони кажуть: "Каркаси какао, як правило, не є безпечними для винятків. Загальна закономірність полягає в тому, що винятки зарезервовані лише для помилок програміста, і програма, що вловлює таке виняток, повинна швидко закритися після цього . "
jpswain

7

Інші згадували, як ви можете встановити прапор або використовувати a goto, але я б рекомендував здійснити рефакторинг коду, щоб внутрішній цикл перетворився на окремий метод. Потім цей метод може повернути деякий прапор, щоб вказати, що зовнішній цикл повинен break. Якщо ви належним чином назвете свої методи, це стане набагато зручнішим для читання.

for (int i = 0; i < 10; i++) {
   if (timeToStop(i)) break;
}

-(bool) timeToStop: (int) i {
    for (int j = 0; j < 10; j++) {
        if (somethingBadHappens) return true;
    }

    return false;
}

Псевдокод не перевірявся, але ви зрозуміли ідею.


3

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

bool isTerminated = false;

for (...)
{
    if (!isTerminated)
    {
        for(...)
        {
            ...

            isTerminated = true;
            break;
        }
    }
    else
    {
        break;
    }
}

2

Змініть лічильник верхнього циклу перед розривом

for(i=0; i<10 ; i++)
  for(j=0;j< 10; j++){
     ..
     ..
     i = 10; 
     break;
  }

13
Мені це не надто подобається. Ризик полягає в тому, що хтось змінює умову виходу в одному місці, а забуває інше.
Йохан Котлінський

NSUInteger limit = 10; Потім:for(i=0; i<limit; i++) { for(j=0; j<10; j++) { .. .. i = limit; break; } }
Метт Мак

2

Напевно, найпростіший спосіб - використовувати змінну "прапор"

for(i=0; i<10 && (done==false); i++)
  for(j=0;j< 10; j++){
     ..
     ..
     if(...){done=true; break;}
  }

@Jonny це fast-enumerationв Obj-C називаються циклами
Альберт Реншоу

2

Іншим рішенням є виділення другого циклу у функції:

int i;

for(i=0; i<10 ; i++){
    if !innerLoop(i) {
        break;
    }
}

bool innerLoop(int i)
    int j;
    for(j=0;j< 10; j++){
        doSomthing(i,j);
        if(endcondtion){
            return false;
        }
    }
}

1

Оператор break виривається із внутрішнього циклу. Для виходу із зовнішнього циклу знадобиться додатковий оператор тестування та розриву.


0

Якщо перерва виконується з набору вкладених циклів, завершується лише найпотаємніший цикл, в якому виконується розрив. (Так само, як стандарт C)


0

Точно так само, як і останні, загалом так:

for(i=0;i<a;i++){  
 for(j=0;j<a;j++){
  if(Something_goes_wrong){
   i=a;
   break;
   }
 }
}

0

Що стосується усмішок, як щодо зміни цієї істинної / помилкової перевірки на метод та використання returnоператорів:

- (bool) checkTrueFalse: parameters{

   for ( ...) {
      for ( ... ) {

         if (...) {
            return true;
         }

      }
   }
   return false;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.