如何摆脱嵌套循环?


96

如果我使用一条break语句,它将仅中断内部循环,而我需要使用一些标志来中断外部循环。但是,如果有许多嵌套循环,则代码将看起来不好。

还有其他方法可以打破所有循环吗?(请不要使用goto stmt。)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2

2
您可以在循环开始之前尝试int i和int j,然后在使它们成为1001的条件下,循环将不会迭代下一个。
Khurram Ijaz'3

Answers:


43

用:

if (condition) {
    i = j = 1000;
    break;
}

49
可行,但丑陋且不通用。如果有人将限制更改为2000(假设代码更长,那么您不会立即注意到它)怎么办?
ugoren

1
@ugoren然后也很简单。如果const int count =1000 在全局初始化中使用,该怎么办。或作为#define宏。
Laksith

4
正如@ugoren指出的,这不是一个通用的解决方案。由于这是该问题的第一个Google命中结果,因此,如果选择了常规解决方案,那就太好了。好吧,人们还是习惯于检查出第二名。
BeeOnRope '16

1
我想只需要i = 1000?
Peter Wu,

189

不,不要用来破坏乐趣break。这是goto;)的最后剩余有效用法。

如果不是这样,则可以使用标志突破深层嵌套循环。

打破嵌套循环的另一种方法是将两个循环分解为一个单独的函数,并在要退出时从该函数返回。

总结-打破嵌套循环:

  1. goto
  2. 使用标志
  3. 分解出循环进入单独的函数调用

无法抗拒在这里包括xkcd :)

在此处输入图片说明

资源

Goto被认为是有害的,但评论中的许多人认为它并不一定有害。如果明智地使用它可能是一个很好的工具。适度使用任何东西都是很有趣的。


29
Goto与您到达这里一样清晰。将exit变量设置为1000会更加麻烦。
correnos 2012年

3
我想补充一点,gotos并不是明确的邪恶,它们只是可以用于邪恶。我发现有很多情况,例如,这是最佳解决方案。“不要使用gotos”是一个不错的开始,但是我认为下一步的技巧是“不要使用远程gotos”。
2012年

1
我不同意这样的观点:“创建函数会导致成倍增加/减少堆栈指针的数量”。如果有一个本地(静态)函数仅在程序流中的某一点被调用,则任何半透明的编译器都将对其进行内联,并且生成的代码与goto本质上相同。对于任何编译器而言,这可能是最简单的优化案例。
DrV

1
重构通常是最干净的解决方案。但是,如果在内循环期间更改了任何外循环变量,事情就会变得复杂。一种可能是通过引用(指针)将变量传递给内部函数,但这可能会混淆编译器优化并产生不必要的额外代码。另一种可能性是在模块级别将此类变量设为静态,但这也不是很漂亮。不幸的是C缺少嵌套函数,因为它们可以解决此问题—除非您愿意使用提供扩展的gcc绑定自己。
DrV

1
+1。Donald E. Knuth的转到语句的结构化编程(wiki.c2.com/?StructuredProgrammingWithGoToStatements)是平衡Dijkstra的有趣的文章。
kmkaplan

40
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}

解决方案仍然会在中断时将两个变量加一,这可能会导致麻烦
TheSola10'3

7
可以设置“ stop = true;” 然后“休息”。然后,在内部“ for”循环结束之后,立即执行“ if(stop)break;”。
杰夫·格里格

34

一种方法是将所有嵌套循环放入函数中,并在需要脱离所有循环的情况下从最里面的循环返回。

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}

1
对我来说似乎是最好的解决方案
卡·斯蒂布


15

如果需要可读性,则需要一个布尔变量:

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

如果您希望其可读性较低,则可以加入布尔评估:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

作为最终方法,您可以使初始循环无效:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}


4

警告:此答案显示了真正模糊的构造。

如果您使用的是GCC,请签出该库。像在PHP中一样,break可以接受要退出的嵌套循环数。您可以这样写:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}

在引擎盖下,它的确使用了goto :)
jacobq

@ iX3如果有帮助,我可以使用内联汇编器和jmp指令。
达伯勒

@DaBler,我不知道你是那个图书馆的作者。我的评论并不是要作为反馈,而是要指出该库使用的方法与接受的答案相同。希望您的评论是在开个玩笑,因为我认为使用语言功能(甚至goto)更适合内联汇编(特定于机器,更容易犯错误,更难阅读...)。
jacobq

3
for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; i++) {
       if(condition) {
            goto end;
   }
} 

end:

3

如果您需要i和j的值,这应该可以工作,但性能要比其他人低

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}

请注意,如果条件取决于条件,j则需要以某种方式存储条件的值才能使其继续工作。
SuperBiasedMan 2015年

1
您说得对,但休息后,j的值不变,条件的值也不变。
Ali ErenÇelik2015年

这是一个破损的解决方案,通常无效。要么j不其环路之外定义的或for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }将要总是内循环的第一次迭代之后打破外环出来。
Chai T. Rex


-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}

因此,基本上,您是说它应该(1)进行一堆额外的函数调用,然后(2)在condition变为false 的其余时间内旋转。哦,第二个循环将永远运行,因为它会递增i而不是j
哎呀

-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

} while ((i < 1000) && !condition);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.