Виривання з вкладеної петлі


216

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

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

Який швидкий і приємний спосіб зробити це?

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

Я не вважаю, що правильно скористатися новими можливостями в .NET (anon методи), щоб зробити щось досить принципове.


Я просто хотів переконатися: чому ти хочеш це робити?
Джон Лімжап

3
Чому ви не хочете використовувати булевий? Що з цим робити?
Антоній

У VB.net ви можете обернути спробу / нарешті оператор (без улову) навколо довільної кількості циклів, тоді "спробу виходу" вийде з усіх у будь-яку точку.
Brain2000

Відповіді:


206

Ну, gotoале це некрасиво, і це не завжди можливо. Ви також можете розмістити петлі в метод (або anon-метод) і використовувати returnдля виходу назад до основного коду.

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

vs:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

Зауважте, що в C # 7 ми повинні отримати "локальні функції", що (синтаксис tbd тощо) означає, що він повинен працювати щось на зразок:

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");

49
У такому типі ситуації я не думаю, що використання goto не гірше, ніж звичайне використання чогось на кшталт розриву (адже вони обидва просто безумовні гілки до мітки, це просто те, що з перервою мітка неявна).
Грег Бук

37
іноді гото є менш злим, ніж альтернативи
seanb

7
@BeowulfOF - прорив буде вириватися лише із внутрішньої петлі, а не із внутрішньої та зовнішньої петель.
Грег Бук

36
Гото саме по собі не потворне. Що некрасиво - це зловживання goto, що призводить до коду спагетті. Використання goto для виходу з вкладеного циклу ідеально нормально. Крім того, зауважте, що всі точки злому, продовження та повернення, з точки зору структурного програмування, навряд чи краще, ніж goto - в основному вони одне і те ж, тільки в більш хорошій упаковці. Ось чому чистим структурним мовам (наприклад, оригінальному Pascal) бракує всіх трьох.
el.pescado

11
@ el.pescado абсолютно прав: багато кодерів введено в оману, вважаючи, що gotoце шкідливо саме по собі , в той час як це просто правильний інструмент для виконання деяких справ, і, як багато інструментів, можна неправильно використовувати. Це релігійне відраза проти gotoвідверто досить дурне і, безумовно, ненаукове.
o0 '.

96

C # адаптація підходу, що часто використовується в C - задане значення змінної зовнішньої петлі поза умовами циклу (тобто для циклу, що використовує змінну int INT_MAX -1, часто є добрим вибором):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

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

Цей підхід працює з forі whileпетлями , але не працює foreach. У випадку, якщо у foreachвас не буде доступу коду до прихованого нумератора, тому ви не можете його змінити (і навіть якщо у вас IEnumeratorне може бути якогось методу "MoveToEnd").

Подяка авторам накреслених коментарів:
i = INT_MAX - 1пропозиція Meta
for / foreachкоментар ygoe .
Правильна IntMaxвід jmbpiano
зауваження про код після внутрішньої петлі бліпастами


@DrG: Не буде працювати в c #, оскільки "Оператор перерви припиняє найближчий контур або заяву перемикання, в якому він відображається." (msdn)
бліпаста

1
@blizpasta Так? Якщо він зробить умову зовнішньої петлі помилковою (як і він), вона вийде з обох.
Патрік

51
ви повинні використовувати i = INT_MAX - 1; в іншому випадку я ++ == INT_MIN <100, і цикл продовжиться
Meta

6
будь-яка ідея на foreach?
ktutnik

4
@ktutnik Це не працюватиме з foreach, оскільки у вас не буде доступу коду до прихованого перелік. Також IEnumerator не має методу "MoveToEnd".
ygoe

39

Це рішення не стосується C #

Для людей, які знайшли це питання за допомогою інших мов, Javascript, Java та D дозволяє мітити перерви та продовжує :

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

40
це сумно, що це неможливо зробити з c #, воно в стільки разів створювало б чистіший код.
Рікард

17
Я насправді хвилювався на секунду, поки не зрозумів, що це НЕ для c #. :(
Арво Боуен

1
Ця концепція також є для PowerShell у випадку, якщо хтось стикається з проблемою. (Вони просто ставлять двокрапку перед назвою мітки.) Тепер я знаю, що це не специфічно для PowerShell ... Цей синтаксис був би несумісний з готовими мітками, доступними в C #. PHP використовує щось інше: перерва 3; Поставте кількість рівнів після заяви про перерву.
ygoe

1
Це java not c #
Luca Ziegler

Для тих, хто хотів би побачити цю функцію в C #, розмістіть свої куди тут, будь ласка, 😉 github.com/dotnet/csharplang/isissue/869#issuecomment-326606003
m93a

28

Використовуйте відповідний щиток у зовнішній петлі. Встановіть оберіг у внутрішню петлю перед тим, як розірватися.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

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

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}

4
За винятком того, що в ОП було сказано: "Я не хочу використовувати булів".
LeopardSkinPillBoxHat

Дуже паскаль-іш ... Я б, мабуть, скоріше скористався б гото, хоча зазвичай уникаю їх, як чума.
Джонатан Леффлер

21

Не цитуйте мене з цього приводу, але ви можете використовувати goto як пропонується в MSDN. Є й інші рішення, зокрема включити прапор, який перевіряється в кожній ітерації обох циклів. Нарешті, ви можете використати виняток як справді важке рішення вашої проблеми.

ЙТИ ДО:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

Хвороба:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

Виняток:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}

2
все це хитрі шляхові шляхи, де було б дуже чисто, щоб просто взяти участь у методі та скористатися достроковим поверненням
Дастін Гец

3
:) правильно, це просте рішення, але вам доведеться передати всі необхідні локальні дані в метод як аргументи ... Це одне з небагатьох місць, де goto може бути відповідним рішенням
David Rodríguez - dribeas

Метод Condition навіть не працює, оскільки "більше коду" буде виконано один раз після виходу з внутрішнього циклу перед виходом із зовнішнього циклу. Метод GOTO працює, але робить саме те, що плакат сказав, що не хоче робити. Метод винятку працює, але гірше і повільніше, ніж GOTO.
Програміст Windows

Я б виділив цю крапку з комою після етикетки. Таким чином ця мітка може бути навіть у кінці блоку. +1
Тамас Гегед

@Windowsprogrammer ОП запитала: "Який швидкий і приємний спосіб зробити це?" Гото - найкраще рішення: чисте і стисле, без залучення окремого методу.
Suncat2000

16

Чи можна перефактурувати вкладений цикл в приватний метод? Таким чином, ви можете просто «повернутися» з методу для виходу з циклу.


З побічною перевагою скорочення вашого оригінального методу :-)
Мартін Каподічі

C ++ 11 лямбдів полегшують це для деяких випадків:[&] { ... return; ... }();
BCS

14

Мені здається, людям дуже не подобається gotoтвердження, тому я відчув необхідність трохи виправити це.

Я вважаю, що "емоції" людей gotoзрештою зводяться до розуміння коду та (помилок) щодо можливих наслідків для продуктивності. Перш ніж відповісти на запитання, я спершу перейду до деяких деталей того, як воно складено.

Як ми всі знаємо, C # компілюється в IL, який потім компілюється в асемблер за допомогою компілятора SSA. Я дам трохи уявлення про те, як це все працює, а потім спробую відповісти на саме запитання.

Від C # до IL

Спочатку нам потрібен фрагмент коду C #. Почнемо з простого:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

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

Перший переклад: з foreachеквівалентного forциклу (Примітка. Тут я використовую масив, тому що я не хочу потрапляти в деталі IDisposable - в такому випадку мені також доведеться використовувати IEnumerable):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

Другий переклад: forі breakперекладається в більш простий еквівалент:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

І третій переклад (це еквівалент коду IL): ми змінюємо breakі whileв галузі:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

Хоча компілятор робить це за один крок, він дає вам уявлення про процес. Код IL, який розвивається з програми C #, - це буквальний переклад останнього коду C #. Переконатися в цьому можна тут: https://dotnetfiddle.net/QaiLRz (натисніть "переглянути IL")

Тепер одне, що ви тут помітили, - це те, що під час процесу код стає складнішим. Найпростіший спосіб це спостерігати за тим, що нам потрібно було все більше і більше коду, щоб прискорити те саме. Можна також стверджувати , що foreach, for, whileі breakнасправді є короткими руками для goto, що частково вірно.

Від ІЛ до Асемблера

Компілятор .NET JIT - компілятор SSA. Я не буду вникати в усі деталі форми SSA тут і як створити оптимізуючий компілятор, це занадто багато, але я можу дати базове розуміння того, що буде. Для глибшого розуміння найкраще почати читати про оптимізацію компіляторів (мені подобається ця книга для короткого вступу: http://ssabook.gforge.inria.fr/latest/book.pdf ) та LLVM (llvm.org) .

Кожен оптимізуючий компілятор покладається на те, що код простий і слід передбачуваних моделей . У випадку циклів FOR ми використовуємо теорію графів для аналізу гілок, а потім оптимізуємо такі речі, як цикл у наших гілках (наприклад, гілки назад).

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

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

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

То чому ж компілятор робить це? Ну, якщо ми зможемо розкрутити цикл, ми можемо його векторизувати. Ми навіть можемо довести, що додаються лише константи, а це означає, що вся наша петля може випасти на повітрі. Підводячи підсумок: зробивши шаблони передбачуваними (зробивши гілки передбачуваними), ми можемо довести, що в нашому циклі існують певні умови, а це означає, що ми можемо робити магію під час оптимізації JIT.

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

На цьому етапі ви також повинні усвідомити, що просте foreachє більш передбачуваним, ніж купа gotoтверджень, які проходять повсюдно. Що стосується (1) читабельності та (2) з точки зору оптимізатора, це і краще рішення.

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

Допомога, занадто велика складність ... що мені робити?

Суть полягає в тому, що ви завжди повинні використовувати мовні конструкції, які у вас є, що зазвичай (неявно) будують передбачувані шаблони для вашого компілятора. Намагайтеся уникати дивних гілок , якщо це можливо ( в Зокрема: break, continue, gotoабо returnв середині нічого).

Хороша новина тут полягає в тому, що ці передбачувані зразки є легкими для читання (для людей) та легкими для помічення (для компіляторів).

Один із таких шаблонів називається SESE, який означає "Single Entry Single Exit".

І тепер ми переходимо до реального питання.

Уявіть, що у вас є щось подібне:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

Найпростіший спосіб зробити цю передбачувану схему - просто усунути ifповністю:

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

В інших випадках ви також можете розділити метод на 2 способи:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

Тимчасові змінні? Хороший, поганий чи потворний?

Ви навіть можете вирішити повернути булевий цикл із циклу (але я особисто віддаю перевагу формі SESE, тому що компілятор це побачить, і я думаю, що це чистіше читати).

Деякі люди вважають, що використовувати тимчасову змінну чистіше, і пропонують таке рішення:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

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

Правильно, дозвольте мені прописати це. Що станеться, це те, що:

  • Компілятор випише все як гілки.
  • В якості кроку оптимізації компілятор зробить аналіз потоку даних, намагаючись видалити дивну moreзмінну, яка буває використана лише в керуючому потоці.
  • У разі успіху змінна moreбуде видалена з програми, а залишаються лише гілки. Ці гілки будуть оптимізовані, тому ви отримаєте лише одну гілку із внутрішньої петлі.
  • Якщо невдало, змінна more, безумовно, використовується в самому внутрішньому циклі, тому якщо компілятор не оптимізує її, вона має високі шанси бути віднесеною до реєстру (що з'їдає цінну пам'ять реєстру).

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

Іншими словами, найкращим сценарієм є те, що він закінчиться еквівалентом цього:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

Моя особиста думка з цього приводу досить проста: якщо це те, що ми мали намір весь час, давайте полегшимо світ як для компілятора, так і для читання, і запишемо це відразу.

tl; dr:

Нижня лінія:

  • Якщо можливо, використовуйте просту умову у своєму циклі. Дотримуйтесь якомога більше мовних конструкцій, які є у вас в розпорядженні.
  • Якщо все виходить з ладу, і ви залишаєтесь з одним gotoабо bool more, віддайте перевагу першому.

OTOH: Я лише рідко маю бажання розірвати зовнішні петлі або побажати goto-equiv (і багато коду, який, можливо, може бути написаний більш чітко), хоча таке може бути більш поширеним в інших областях .. сьогодні такий випадок був, але це багато в чому було пов'язано yield return. Тобто, хоча це легко показати, що є деякі корисні випадки, для більшості кодів на "додатку" це, мабуть, дуже низький рівень фрустрації C #, виключаючи ті, що "щойно надходять від" C / C ++ ;-) Я також час від часу сумую Рубі "кидок" (не виняток), коли він також вписується в цю область.
користувач2864740

1
@ Suncat2000 Просто введення більшої кількості коду, таких як змінні та умовні гілки, щоб уникнути використання goto, не робить ваш код читабельнішим - я б заперечував, що трапляється прямо навпаки: адже ваш контрольний потік буде містити більше вузлів - що в значній мірі визначення складності. Просто уникати цього, щоб допомогти самодисципліні, звучить протилежно з точки зору читабельності.
atlaste

@atlaste: Вибачте, дозвольте чіткіше висловити свій коментар. Що я мав сказати: приємне пояснення. Але люди, які уникають гото, не стосуються продуктивності; йдеться про уникнення зловживань, що викликає недостатню читабельність. Ми можемо припустити, що люди, які уникають gotoпросто не мають дисципліни, щоб не зловживати цим.
Suncat2000

@atlaste, яка мова "ackomplish"?
Pacerier

11

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

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return

10

Ви попросили поєднати швидке, приємне, без булевого використання, без використання goto та C #. Ви виключили всі можливі способи робити те, що ви хочете.

Найшвидший і найменш негарний спосіб - використовувати гото.


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

@Windowsprogramm: OP не просив "не використовувати goto". Він не хотів "переходити до іншого методу". Питання було далеко не тим, що виключає всі можливі шляхи, але ви маєте право надати на увазі, що тут найкраще перейти до goto.
Suncat2000

5

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

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}

1
але тоді це дозволяє традиційне рішення, яке просить ОП
Сурія Пратап

2

Я бачив багато прикладів, які використовують "break", але жоден, який використовує "продовжити".

Це все-таки вимагатиме певного прапора у внутрішньому циклі:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}

1

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

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.

3
Тоді програміст технічного обслуговування вставить інший рівень введення, виправить деякі заяви про перерву та порушить деякі інші заяви про перерву. Виправленням цього є замість цього мітка. Це дійсно було запропоновано, але прагматики натомість використовують goto label.
програміст Windows

Зачекайте, хто більше програмує технічне обслуговування? :)
Джессі К. Слікер

2
JavaScript навіть має мітки блоків / заяви про перерви. devguru.com/Technologies/ecmascript/quickref/break.html
Девід Грант

@Chris Bartow: здорово! зробив моє Різдво :) @David Grant: так здається, JS перерва == С іти?
Джессі К. Слікер

D позначив перерву / продовження
BCS

0

З моїх студентських днів я пам’ятаю, що з математичних доказів можна сказати, що ви можете робити що-небудь у коді без goto (тобто немає ситуації, коли goto є єдиною відповіддю). Отже, я ніколи не використовую Goto (просто мої особисті переваги, не підказуючи, що я правильно чи неправильно)

У будь-якому випадку, щоб вирватися з вкладених циклів, я роблю щось подібне:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... сподіваюся, що для тих, хто подобається мені, є анти-гото "фанбої" :)


Вибачте, що розповідаю, але ваш лектор винен у вашому стані. Якщо він би не змусив вас навчитися монтажу, то ви знаєте, що "goto" - це просто стрибок (звичайно, я ігнорую той факт, що це питання # AC).
cmroanirgo

0

Ось як я це зробив. Досі вирішення.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}


0

Найпростішим способом закінчити подвійну петлю було б безпосередньо закінчення першого циклу

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}

Це не працює, оскільки за допомогою перерви ви закінчите внутрішню петлю. Зовнішня петля продовжуватиме повторюватися.
Олексій Лев

0

Петлі можуть бути розірвані, використовуючи власні умови в циклі, дозволяючи мати чистий код.

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

За допомогою цього коду ми отримуємо такий вихід:

  • Ітерація зовнішньої петлі 0
  • Ітерація внутрішньої петлі 0
  • Ітерація внутрішньої петлі 1
  • Ітерація внутрішньої петлі 2
  • Ітерація внутрішньої петлі 3
  • Ітерація внутрішньої петлі 4
  • Код після внутрішнього циклу буде виконуватися при перервах

0

Іншим варіантом є анонімна функція, яка самостійно викликається . Ні goto, ні мітка, ні нова змінна, ні нове ім'я функції не використовується, а на один рядок коротший, ніж приклад анонімного методу вгорі.

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");

-3

Закиньте спеціальний виняток, який виходить із зовнішнього циклу.

Він працює for, foreachабо whileчи будь-який вид циклу і будь-яку мову , який використовує try catch exceptionблок

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }

У чому суттєва різниця у відповіді dviljoen?
Герт Арнольд

Це не спрацює, тому що ви не перевіряєте стан "breakInnerLoop" у зовнішній частині для циклу, тому ви просто перейдете до наступного циклу, також ви написали j та J та пропустили крапку з комою, яка не складеться.
Себастьян

-4

Як я бачу, ви прийняли відповідь, в якій людина посилається на висловлення goto, де в сучасному програмуванні та на думку експерта goto є вбивцею, ми назвали це вбивцею в програмуванні, яке має певні причини, і я не буду це обговорювати тут на даний момент, але рішення вашого питання дуже просте, ви можете використовувати булевий прапор у такому сценарії, як я продемонструю це у своєму прикладі:

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

простий і простий. :)


прочитайте питання, перш ніж запропонувати перерву. Звідси низості.
cmroanirgo

1
прочитайте його зараз, але коли я опублікував цю відповідь, відредаговану версію, що не використовує булеву, не було додано, ось чому я опублікував цю відповідь .. але дякую за downvote!
Стів

1
Це не особиста річ. На жаль, голосування зараз заблоковано;) Це необхідна частина ТА, щоб отримати найкращі відповіді до вершини (що не завжди буває)
cmroanirgo

-6

Ви навіть переглянули breakключове слово? Оо

Це просто псевдо-код, але ви повинні мати можливість бачити, що я маю на увазі:

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

Якщо ви думаєте про breakте, як функція на зразок break(), то її параметром буде кількість циклів, які потрібно вирвати. Оскільки ми знаходимося в третьому циклі коду тут, ми можемо вирватися з усіх трьох.

Посібник: http://php.net/break


13
Не питання php.
Зерга

-10

Я думаю, якщо ви не хочете робити "булеву справу", єдине рішення - насправді кинути. Що ви, очевидно, не повинні робити ..!

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