Как завершить внешний цикл во вложенных циклах?
Как лучше всего завершить все вложенные циклы в примере ниже. Как только оператор 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
, и я использовал для этого, но на это смотрят неодобрительно. Что глупо; этот сценарий почему она существует в языке.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