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


Как лучше всего завершить все вложенные циклы в примере ниже. Как только оператор if истинен, я хочу завершить внешний оператор for (с I). Другими словами, Мне нужно, чтобы вся петля остановилась. Есть ли лучший способ, чем установка I на 10?

for (int I = 0; I < 10; I++)
{
    for (int A = 0; A < 10; A++)
    {
        for (int B = 0; B < 10; B++)
        {
            if (something)
                break;
        }
    }
}
14 14

14 ответов:

Я бы рефакторировал это в метод и просто вызывал return всякий раз, когда мне это нужно.

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

void DoSomeStuff()
{
    for (int I = 0; I < 10; I++)
    {
        for (int A = 0; A < 10; A++)
        {
            for (int B = 0; B < 10; B++)
            {
                if (something)
                    return;
            }
        }
    }
}
...somewhere else...
DoSomeStuff();

Не стреляйте в меня, но это может действительно гарантировать goto:

 for (int I = 0; I < 10; I++) {
      for (int A = 0; A < 10; A++) {
           for (int B = 0; B < 10; B++) {
               if (something)
                   goto endOfTheLine;
            }
      }
  }
  endOfTheLine:
  Console.WriteLine("Pure evilness executed");

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

bool done = false;
for (int i = 0; i < 10 && !done; i++) {
    for (int a = 0; a < 10 && !done; a++) {
        for (int b = 0; b < 10 && !done; b++) {
            if (something) {
                done = true;
                continue;
            }
        }
    }
}

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

var query = from I in Enumerable.Range(0, 10)
            from A in Enumerable.Range(0, 10)
            from B in Enumerable.Range(0, 10)
            where something(I, A, B)
            select new { I, A, B };
var result = query.FirstOrDefault();
if (result == null)
{
   Console.WriteLine("no result");
}
else
{
    Console.WriteLine("The first result matching the predicate was {0} {1} {2},
        result.I, result.A, result.B);
}
Но не делайте этого, если циклы имеют побочные эффекты; запросы-это действительно плохое место для размещения побочных эффектов. Если внутренняя петля имеет побочный эффект, то вы можете сделать что-то вроде этого:
var triples = from I in Enumerable.Range(0, 10)
              from A in Enumerable.Range(0, 10)
              from B in Enumerable.Range(0, 10)
              select new { I, A, B };
foreach(var triple in triples)
{
    if (something(triple.I, triple.A, triple.B))
        break;
    DoSomeSideEffect(triple.I, triple.A, triple.B);
}
И теперь есть только одна петля, из которой нужно вырваться, а не три.

Почему бы не сделать:

 for (int I = 0; I < 10 || !something; I++)
        {
            for (int A = 0; A < 10 || !something; A++)
            {
                for (int B = 0; B < 10; B++)
                {
                    if (something)
                    {
                       I=10;
                       break;
                    }
                }
            }
        }

Вы всегда можете использовать тот факт, что в for есть условное утверждение таким образом:

bool working = true;
for (int i=0; i<10 && working; i++) 
{
    for (int j=0; j<10 && working; j++) 
    {
        for (int k=0; k<10 && working; k++) 
        {
            Console.WriteLine(String.Format("i={0}, j={1}, k={2}", i,j,k));
            if (i==5 && j==5 && k==5) 
            {
                working = false;
            }
        }
    }
}

Я бы склонялся в пользу goto также иначе вам придется выйти из каждого цикла:

    for (int I = 0; I < 10; I++)
    {
        for (int A = 0; A < 10; A++)
        {
            for (int B = 0; B < 10; B++)
            {
                if (something)
                    break;
            }
            if (something)
                break;
        }
        if (something)
            break;
    }

Если это конечная задача в методе, то вы можете вернуться, когда условие истинно. в противном случае вы должны привести все значения к максимальным значениям

if (something)              
    {
        I=10;   
        B=10;
        A=10;
        break;
    }
for (int I = 0; I < 10; I++) {     
     for (int A = 0; A < 10; A++)     {         
         for (int B = 0; B < 10; B++)         {             
            if (something){                 
                  B=13;
                  A=13;
                  I=13;
             }
          }     
     } 
 } 

Очень примитивное решение.

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

В моем случае я предположу, что вы хотели получить значения I, A и B в этой точке, тривиальные с кортежем вместо этого.

// original method
...
var x = FindFirst()
...

// separate method
public Tuple<int,int,int> FindFirst()
{
    for (int I = 0; I < 10; I++)
    {
        for (int A = 0; A < 10; A++)
        {
            for (int B = 0; B < 10; B++)
            {
                if (something)
                    return Tuple.Create(I,A,B);
            }
        }    
    }
    return null;
}

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

Если вы хотите справиться с неспособностью найти первый в a другая мода тогда что-то вроде

bool TryFindFirst(out Tuple<int,int,int> x) 

Будет альтернативой.

В качестве примечания использование заглавных букв для имен переменных (особенно однобуквенных) считается плохим стилем в c# (и многих других языках)

Я не знаю, поддерживает ли его C#, но некоторые языки поддерживают:

break n;

Где n это число вложенных циклов для разрыва.

Вы всегда можете соответствовать ожиданиям циклов:

Если (что-то) B = 10

Edit: (Похоже, вы включили это в свой пост через редактирование)

Если вам не нравится, как это выглядит, вы можете обернуть функцию, такую как:

Удовлетворяют(B, 10)

Тогда он выглядит чище, но на самом деле не нужен.

Другая возможность-каскадировать проверку на isSomething во всех циклах for. вы добавляете

if (something)                         
   break; 

Во всех 3 петлях

Лично я бы пошел с методом Paxdiablo выше (+1 для этого), но альтернатива ниже - это зависит от того, должен ли ОП знать, что числа I, A и B, когда "что-то" истинно, потому что iab были объявлены в цикле, я предполагаю, что нет.

bool done = false;
int i, a, b;
for (i = 0; i < 10 ; i++) {
    for (a = 0; a < 10 ; a++) {
        for (b = 0; b < 10 ; b++) {
            if (something) {
                done = true;
                break;
            }
        }
        if (done) break;
    }
    if (done) break;
}
// i, a and B are set to the last numbers where "something" was true