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

каков наилучший способ завершить все вложенные циклы в приведенном ниже примере. Как только утверждение 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 ответов


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

вы также можете использовать goto, а я есть использовать 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;
}

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

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

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

будет другой.

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


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

break n;

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


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

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

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

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

Удовлетворить(B, 10)

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


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

if (something)                         
   break; 

во всех 3 петли


лично я бы пошел с методом Paxdiablo выше (+1 для этого), но альтернатива ниже - это зависит от того, должен ли OP знать, что такое числа 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