如何在Objective-C中打破两个嵌套的for循环?


74

我有两个这样嵌套的for循环:

for(...) {
    for(...) {

    }
}

我知道有一个break声明。但是我很困惑它是否打破了两个循环,或者只是打破了它被调用的那个循环?一旦发现重复进行多次没有意义,就需要将两者都打破。

Answers:


94

break打破了一个循环,但是您可以向外部循环添加一个检查,当内部破裂时,该检查会破裂。

bool dobreak = false;
for ( ..; !dobreak && ..; .. ) {
   for ( ... ) {
      if (...) {
         dobreak = true;
         break;
      }
   }
}

56
恕我直言,使用goto更加清洁。
sigjuice,2009年

4
!dobreak在错误的地方;它应该在有条件的(用于第二部分)中而不是在增量步骤(对于第三部分)中进行;我还将使用!dobreak && ..,这样在打破时就不需要评估其他条件。我同意sigjuice:goto正确使用它并不有害,在这种情况下goto 确实可以编写更好的代码。
迅速

2
这仍然会在内部循环之后执行可能在外部循环中执行的代码
Martijn 2010年

@Martijn -这可以通过更换得到解决breakcontinue声明。不过,这只是一些非显而易见的问题而值得担心。
2011年

107

如果使用goto简化了代码,那将是适当的。

for (;;) 
{
    for (;;) 
    {
        break; /* breaks inner loop */
    } 
    for (;;) 
    {
        goto outer; /* breaks outer loop */
    }
} 
outer:;

4
为了对此进行扩展,for (;;) {for (;;) {break; /* breaks inner loop */} for (;;) {goto outer; /* breaks outer loop */}} outer:;
短暂

7
(;;)看起来像张小脸。
zakdances

14

break语句只会使您退出最内层的循环。如果您不希望在代码,内存和专用状态变量的性能方面增加开销,我建议将代码重构为自己的函数或方法,并使用它return来摆脱所有循环:

void do_lots_of_work(void)
{
  int i, j;

  for(i=0; i<10 ; i++)
  {
    for(j=0;j< 10; j++)
    {
     ..
     ..
     if(disaster_struck())
      return; /* Gets us out of the loops, and the function too. */
    }
  }
}

如果循环中的函数调用能够像专用状态变量一样成功执行,我会感到非常惊讶,特别是如果您需要传递参数才能访问外部函数中的变量时,我会感到非常惊讶。也许如果编译器复制了内联函数...
短暂

6
在我看来,更糟糕的是失去了通过一处读取算法来理解算法的能力。所有这些避免去吗?
Ori Pessach,2009年

我也遇到了这个中断/继续问题,有时想继续循环2-3级。恕我直言,最好的办法是将嵌套循环重构为方法,以便该方法可以返回是否继续到父循环。
杰森

如果用户只想退出循环而不退出函数怎么办?
Akshay J

我认为这是最好的解决方案。发生的事情是明确的,易于控制,易于调用,不分配和评估不必要的布尔值,并且不使用危险的古老做法,例如goto。在任何实际使用中,对性能的关注也应该是不明显的。
Ben Leggiero

9

除了已经提到的flag变量或goto之外,您还可以引发Objective-C异常:

@try {
  for() {
    for() {
       @throw ...
    }
  }
}
@catch{
  ...
}

5
哇,就像我要修改答案一样,指出人们将如何建议每个复杂的解决方案,除了使用异常进行流控制以免发生goto ...
Ori Pessach,2009年

4
@Ori Pessach好吧,例外是goto的现代OO版本;-)
lothar

1
至少,它们确实很好地清理了堆栈。
Ori Pessach,2009年

2
以我的经验,异常非常慢,不应用于处理执行流程。
娜塔莉·亚当斯

3
对ObjC中的异常进行一些研究。在Apple文档中,他们特别指出性能损失很大,他们说:“ Cocoa框架通常不是异常安全的。通常的模式是,异常仅保留给程序员使用,并且捕获此类异常的程序应在不久后退出。 。”
jpswain

7

其他人提到了如何设置标志或使用goto,但是我建议重构代码,以便将内部循环转换为单独的方法。然后,该方法可以返回一些标志,以指示外部循环应如此break。如果适当地命名方法,则可读性更高。

for (int i = 0; i < 10; i++) {
   if (timeToStop(i)) break;
}

-(bool) timeToStop: (int) i {
    for (int j = 0; j < 10; j++) {
        if (somethingBadHappens) return true;
    }

    return false;
}

伪代码,未经测试,但是您可以理解。


3

break语句将仅跳出作用域中的父循环。如果您也想跳出第二个循环,则可以使用两个循环都包含的布尔变量

bool isTerminated = false;

for (...)
{
    if (!isTerminated)
    {
        for(...)
        {
            ...

            isTerminated = true;
            break;
        }
    }
    else
    {
        break;
    }
}

2

休息前更改顶层循环的计数器

for(i=0; i<10 ; i++)
  for(j=0;j< 10; j++){
     ..
     ..
     i = 10; 
     break;
  }

13
我不太喜欢这个。风险是有人在一个地方更改出口条件,而忘记另一个地方。
约翰·科特林斯基

NSUInteger limit = 10; 然后:for(i=0; i<limit; i++) { for(j=0; j<10; j++) { .. .. i = limit; break; } }
Matt Mc

2

可能最简单的方法是使用“标志”变量

for(i=0; i<10 && (done==false); i++)
  for(j=0;j< 10; j++){
     ..
     ..
     if(...){done=true; break;}
  }

@Jonny那些被称为fast-enumeration中的OBJ-C环
阿尔伯特·伦肖

2

另一个解决方案是在函数中排除第二个循环:

int i;

for(i=0; i<10 ; i++){
    if !innerLoop(i) {
        break;
    }
}

bool innerLoop(int i)
    int j;
    for(j=0;j< 10; j++){
        doSomthing(i,j);
        if(endcondtion){
            return false;
        }
    }
}


0

如果从一组嵌套循环中执行中断,则仅终止执行中断的最里面的循环。(就像标准C一样)


0

就像最后一个一样,通常是这样的:

for(i=0;i<a;i++){  
 for(j=0;j<a;j++){
  if(Something_goes_wrong){
   i=a;
   break;
   }
 }
}

0

仅出于笑容,如何将这种true / false检查更改为方法并使用return语句:

- (bool) checkTrueFalse: parameters{

   for ( ...) {
      for ( ... ) {

         if (...) {
            return true;
         }

      }
   }
   return false;
}
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.