2009-10-19 3 views
15

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

, что я делаю, это использовать флаги так:

int i, j, k; 
int flag1 = 0; 
int flag2 = 0; 

for (i = 0; i < 100; i++) { 
    for (j = 0; j < 100; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) { 
       flag1 = 1; 
       flag2 = 1; 
       break; 
      } 
     } 
     if (flag1 == 1)break; 
    } 
    if (flag2 == 1)break; 
} 

Я не думаю, что это особенно аккуратно.

Как бы вы выполнили то же самое? (без использования переходов)

+8

какой язык? , – newacct

+0

да, на каком языке –

+1

я имел в виду C, что я написал вопрос ... помогает ли какой-либо язык поддерживать это лучше? – Moeb

ответ

85

использование goto. это чисто и просто.

+2

Серьезно? Когда-либо слышал о том, что Эдсгер Дейкстра «Go To Ded Harmful» - http://en.wikipedia.org/wiki/Considered_harmful – Asaph

+36

Так много людей думают, что куча флагов как-то лучше, чем просто, из-за некоторой риторики, которую они когда-либо слышали –

+1

нет, нет адреса пожалуйста. его чистый и простой, но нездоровый **. – Moeb

17

Если вы используете Java, вы можете связывать метки с каждым для блока, а затем ссылаться на метку после инструкции continue. Например:

outerfor: 
for (int i=0; i<5; i++) { 
    innerfor: 
    for (int j=0; j<5; j++) { 
     if (i == 1 && j == 2) { 
      continue outerfor; 
     } 
    } 
}
+0

Многие языки (например, Perl) допускают такое поведение ярлыков, что хорошо, и его следует использовать там, где это применимо. Поэтому я бы добавил +1, если это применимо к C. (Я знаю, что OP интересуется другими языками, но его, вероятно, больше интересует C.) –

+1

@ Крис Лутц: достаточно справедливо. FWIW: Когда OP первоначально размещался, не было никакого упоминания о том, какой язык он использовал.Отсюда все комментарии, спрашивающие, на каком языке. Тэг c был добавлен после публикации. – Asaph

+0

Этот же трюк работает и в JavaScript :) – ephemient

5

Просто немного лучше.

int i, j, k; 
int flag1 = 0; 
int flag2 = 0; 

for (i = 0; i < 100 && !flag2; i++) { 
    for (j = 0; j < 100 && !flag1; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) { 
       flag1 = 1; 
       flag2 = 1; 
       break; 
      } 
     } 
    } 
} 

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

+1

На мой взгляд, это не лучше. Исключительный объем материала, который помещается в выражение 'for', начинает выглядеть как линейный шум. –

+1

Какой смысл иметь несколько флагов? Просто имейте это. Назовите его 'done'. Гораздо яснее. – Alan

+0

просто скопировал образец. Я предполагаю, что флаги не так просты и просто представляют действительный код. – moogs

49

Поместите все петли в функцию и просто верните вместо перерыва.

+8

Обратите внимание, что, хотя это аккуратно избегает явного 'goto', это по сути делает то же самое, поэтому, если вы считаете, что это хорошее решение вы действительно не можете делать никаких аргументов против решения 'goto'. И обратите внимание, что 'goto' может быть предпочтительным в случае сложного циклического поведения, которое может сделать вызов функции менее понятным или простым. –

+4

Разница в том, что вы не можете злоупотреблять возвратом так, как вы можете злоупотреблять goto. Кстати, у Java есть выраженный оператор break, который позволяет вам вырваться из вложенных циклов, но не перейти. – starblue

+7

В C++ return лучше, потому что он будет уважать удаление стека - и таким образом уничтожить любые локально ограниченные переменные, объявленные в любом из циклов. – philsquared

14

Как бы вы выполнили то же самое? (без использования прыжков)

Почему? Ничто не является всеобщим злом, и каждый наложенный инструмент использует его (кроме gets()). Используя goto, ваш код выглядит более чистым и является одним из единственных вариантов, которые мы имеем (принимая C). Смотри:

int i, j, k; 

for (i = 0; i < 100; i++) { 
    for (j = 0; j < 100; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) { 
       goto END; 
      } 
     } 
    } 
} 
END: 

намного чище, чем все эти флаговых переменных, и даже показывает более ясно, что ваш код делает .

+1

И как бы вы реорганизовали это? Вы НЕ ДЕЙСТВИТЕЛЬНО хотите 3 вложенных цикла в вашем коде? – Bostone

+3

Я не знаю, как реорганизовать это, потому что OP не очень специфичен в том, что его три вложенные петли _doing_ в его коде. Он просто хочет вырваться из них изнутри одного из них, поэтому я даю ответ. Некоторые операции _require_ O (n^3) время для достижения, но в конечном итоге достаточно быстро на практике, потому что они обычно работают с небольшими наборами данных и/или выполняемая операция проста и легко оптимизирована. Не пытайтесь реорганизовать то, что ясно и чисто, и прекрасно работает. –

3

иногда вы можете использовать трюк, как это:

for (i = 0; i < 100 && !flag2; i++) { 
for (j = 0; j < 100 && !flag1; j++) { 
    for (k = 0; k < 100; k++) { 
     if (k == 50) { 
      k = 100; 
      i = 100; 
      j = 100; 
     } 
    } 
} 

}

или объявить флаг добавления в цикле:

bool end = false; 
for(int i =0; i < 1000 && !end; i++) { 
    //do thing 
    end = true; 
} 

это стоит только линию, но чистый, я думаю.

джастин

-1

Один из способов сделать это является состояние машины. Но я все равно буду использовать goto. Это намного проще. :)

state = 0; 
while(state >= 0){ 
    switch(state){ 
     case 0: i = 0; state = 1; // for i = 0 
     case 1: 
      i++; 
      if (i < 100) // if for i < 100 not finished 
       state = 2; // do the inner j loop 
      else 
       state = -1; // finish loop 
     case 2: j = 0; state = 3; // for j = 0 
     case 3: 
      j++; 
      if (j < 100) // if j < 100 not finished 
       state = 4 // do the inner k loop 
      else 
       state = 1; // go backt to loop i 
      break; 
     case 4: k = 0; state = 5; 
     case 5: 
      k++; 
      if (k == 50){ 
       state = -1; 
       break; 
      } 
      if (k < 100) // if k loop not finished 
       state = 5; // do this loop 
      else 
       state = 3; // go back to upper loop 
      break; 
     default : state = -1; 
    } 
} 
+0

В чем преимущество государственной машины над goto? – Alphaneo

+0

Фактически нет. Это просто еще один способ сделать эти петли и вырваться без goto. Я бы сделал это с goto. В некоторых случаях лучше использовать это. Например, если вы много прыгаете из одного цикла в другой, например regExp. RegExp-парсеры компилируют эти машины, потому что они работают быстро, и их можно оптимизировать. – Egon

4

goto. Это одно из немногих мест, где goto является подходящим инструментом, и, как правило, аргумент представлен, почему goto не является полным злом.

Иногда, правда, я делаю это:

void foo() { 
    bar_t *b = make_bar(); 
    foo_helper(bar); 
    free_bar(b); 
} 

void foo_helper(bar_t *b) { 
    int i,j; 
    for (i=0; i < imax; i++) { 
     for (j=0; j < jmax; j++) { 
      if (uhoh(i, j) { 
       return; 
      } 
     } 
    } 
} 

Идея заключается в том, что я получаю гарантированный свободный от бара, плюс я получаю чистый перерыв два уровня из коммутатора через возвращение.

0

Разделение на 0 является самым надежным методом, который я знаю, что сломает вас из любой количество петель. Это работает, потому что инструкция сборки DIV не любит такой глупости.

Таким образом, вы можете попробовать это:

int i, j, k; 
int flag1 = 0; 
int flag2 = 0; 

for (i = 0; i < 100; i++) { 
    for (j = 0; j < 100; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) { 
       flag1 = 1; 
       flag2 = 1; 
       int z = 1/0; // we're outta here!!! 
      } 
     } 
     if (flag1 == 1)break; 
    } 
    if (flag2 == 1)break; 
} 

Возвращаясь из trap, что происходит на таких мероприятиях в качестве упражнения для читателя (это тривиально).

+0

Достаточно хороший компилятор не должен компилировать это (так как это недопустимое постоянное выражение), кроме этого исключения никогда не будут использоваться в качестве замены логики управления. Консультирование людей для этого кажется DIVilish для меня :-) – jdehaan

+1

hahaha, золотой. –

+0

Я голосую за это, потому что это так смешно. – Justicle

2

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

int i, j, k; 
for (i = 0; i < 100; i++) { 
    for (j = 0; j < 100; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) 
       break; 
     } 
     if (k < 100) break; 
    } 
    if (j < 100) break; 
} 

По моему опыту, это то, что необходимо в большинстве случаев.

2

немного глупых самодокументированно:

int i, j, k; 
int done = 0; 

for (i = 0; i < 100 && ! done; i++) { 
    for (j = 0; j < 100 && ! done; j++) { 
     for (k = 0; k < 100 && ! done; k++) { 
      if (k == 50) we_are(done); 
     } 
    } 
} 

//... 

void we_are(int *done) { 
    *done = 1; 
} 

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

Хотя я согласен, что иногда goto действительно лучшее решение, я думаю, что любая проблема, к которой относится решение goto, является результатом плохого кода.

+1

+1 для отличного кода. хороший. – elcuco

0

Я хотел бы сделать что-то вроде:

int i, j, k; 

    for (i = 0; i < 100; i++) { 
     for (j = 0; j < 100; j++) { 
      for (k = 0; k < 100; k++) { 
       if (k == 50) { 
        return; 
       } 
      } 
     } 
    } 
4

Если вы совершенно не хотите использовать Гото, установить все условия цикла к ложным:

int i, j, k; 

for (i = 0; i < 100; i++) { 
    for (j = 0; j < 100; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) { 
       i = j = k = INT_MAX; 
       break; 
      } 
     } 
    } 
} 

Примечание: умный оптимизирующий компилятор будет поверните содержимое if в прыжке до конца самого внешнего контура

0

Если вы используете GCC и this library, break может принять количество вложенных циклов, которые вы хотите выйти:

int i, j, k; 

for (i = 0; i < 100; i++) { 
    for (j = 0; j < 100; j++) { 
     for (k = 0; k < 100; k++) { 
      if (k == 50) { 
       break(3); 
      } 
     } 
    } 
}