如何在嵌套循环中终止外循环

本文关键字:终止 循环 嵌套循环 | 更新日期: 2023-09-27 17:58:13

在下面的例子中,终止所有嵌套循环的最佳方式是什么。一旦if语句为true,我想终止外部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;
        }
    }
}

如何在嵌套循环中终止外循环

我会将其重构为一个方法,只要需要就调用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;
            }
        }
    }
}

如果循环体没有产生副作用,而只是在寻找"something"为true的第一个值,那么将通过首先消除所有循环来解决问题。

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;
    }

如果这是方法中的最后一个任务,则可以在条件为true时返回。否则,您必须将所有值设置为最大值

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的值,而不是Tuple。

// 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;
}

如果需要将任何附加状态传递给方法(对于边界或something位),只需将它们作为参数传递即可。

如果你想以不同的方式处理找不到第一个的问题,那么就用这样的方式

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

将是一个替代方案。

顺便说一句,在c#(和许多其他语言)中,使用大写字母作为变量名(尤其是单字母)被认为是糟糕的风格。

我不知道C#是否支持它,但有些语言支持:

break n;

其中n是要中断的嵌套循环数。

您总是可以满足循环的期望:

如果(某事)B=10

编辑:(看起来你通过编辑将其包含在你的帖子中)

如果你不喜欢它的外观,你可以包装一个函数,比如:

满足(B,10)

然后它看起来更干净,但确实不需要。

另一种可能性是在所有for循环中级联对isSomething的检查。你添加

if (something)                         
   break; 

在所有3个循环中

就我个人而言,我会使用上面的Paxdiablo方法(+1),但下面是另一种选择-这取决于OP是否需要知道当"something"为真时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