【问题标题】:How to break out of nested loops?如何打破嵌套循环?
【发布时间】:2012-03-30 13:18:28
【问题描述】:

如果我使用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

【问题讨论】:

  • 你可以在循环开始之前尝试 int i 和 int j ,然后在条件下将它们设为 1001 循环不会迭代下一个。
  • 我有一位教授最讨厌的是你不能发送一个参数来打破,以表明你可以去多少级。如果他有他的方式,你的问题的答案就是“break(2);”,其中“2”是要跳出的循环数。

标签: c nested-loops break


【解决方案1】:

我注意到问题很简单,“还有其他方法可以打破所有循环吗?”我没有看到任何限定条件,但它不是goto,特别是 OP 没有要求 good 方式。那么,我们如何longjmp 走出内循环呢? :-)

#include <stdio.h>
#include <setjmp.h>

int main(int argc, char* argv[]) {
  int counter = 0;
  jmp_buf look_ma_no_goto;
  if (!setjmp(look_ma_no_goto)) {
    for (int i = 0; i < 1000; i++) {
      for (int j = 0; j < 1000; j++) {
        if (i == 500 && j == 500) {
          longjmp(look_ma_no_goto, 1);
        }
        counter++;
      }
    }
  }
  printf("counter=%d\n", counter);
}

setjmp 函数返回两次。第一次,它返回 0 并且程序执行嵌套的 for 循环。然后当ij 都是500 时,它执行longjmp,这导致setjmp 再次返回值为1,跳过循环。

longjmp 不仅可以让您摆脱嵌套循环,还可以与嵌套函数一起使用!

【讨论】:

    【解决方案2】:

    另一种方法是将代码从两个 for 循环重构为一个 for 循环和一个手动循环。这样,手动循环中的中断适用于外部循环。我在 Gauss-Jordan Elimination 中使用过一次,需要三个嵌套循环来处理。

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

    【讨论】:

    • 如果你要去goto,为什么不直接把goto排除在外?
    【解决方案3】:

    我认为goto会解决问题

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

    【讨论】:

    【解决方案4】:

    用途:

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

    【讨论】:

    • 有效,但丑陋且不一般。如果有人将限制更改为 2000(假设代码更长,所以您不会立即注意到)怎么办?
    • @ugoren 那就这么简单。如果您在全局初始化中使用const int count =1000 会怎样。或作为#define 宏。
    • 正如@ugoren 指出的那样,这不是一个通用的解决方案。由于这是该问题的第一次谷歌点击,如果选择了通用解决方案会很好。好吧,无论如何人们都习惯于查看#2。
    • 我猜只需要 i=1000?
    【解决方案5】:

    不,不要用break 破坏乐趣。这是goto 的最后一次有效使用;)

    如果不是这样,那么您可以使用标志来打破深层嵌套循环。

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

    总结 - 打破嵌套循环:

    1. 使用goto
    2. 使用标志
    3. 将循环分解为单独的函数调用

    忍不住在这里加入 xkcd :)

    source

    Goto's are considered harmful 但正如许多 cmets 中的人所暗示的那样,不必如此。如果使用得当,它可能是一个很好的工具。适度使用任何东西都很有趣。

    【讨论】:

    • Goto 就像你在这里一样清晰,是的。将退出变量设置为 1000 更加麻烦。
    • 我想补充一点,goto 并不是明确的邪恶,它们只是可以用于邪恶。我发现有很多情况,例如这个,它们是最好的解决方案。 “不要使用 goto”是一个好的开始,但我认为下一步的技能可以让你“不要使用远程 goto”。
    • 我不同意这一点:“创建一个函数会导致指数级的加/减堆栈指针”。如果有一个本地(静态)函数只在程序流中的一个点被调用,任何半体面的编译器都会内联它,并且生成的代码基本上与 goto 相同。对于任何编译器来说,这可能是最简单的优化案例。
    • 重构通常是最干净的解决方案。但是,如果在内循环期间更改了任何循环外变量,事情就会变得复杂。一种可能性是通过引用(指针)将变量传递给内部函数,但这可能会混淆编译器优化并产生不必要的额外代码。另一种可能性是在模块级别使这些变量静态,但这也不是很漂亮。不幸的是,C 缺少嵌套函数,因为它们可以解决这个问题——除非你愿意将自己绑定到使用提供扩展的 gcc。
    • +1。 Donald E. Knuth 的带 go to Statements (wiki.c2.com/?StructuredProgrammingWithGoToStatements) 的结构化编程是平衡 Dijkstra 的有趣文章。
    【解决方案6】:

    注意:这个答案显示了一个真正晦涩的结构。

    如果您使用的是 GCC,请查看 this library。 就像在 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);
           }
       }
    }
    

    【讨论】:

    • 在引擎盖下确实是using goto :)
    • @iX3 如果有帮助,我可以使用内联汇编程序和 jmp 指令。
    • @DaBler,我不知道你是那个库的作者。我的评论并不是作为反馈,而是指出该库使用与the accepted answer 相同的方法。希望您的评论是个玩笑,因为我认为使用语言功能(甚至goto)最好内联asm(特定于机器,更容易出错,更难阅读......)。
    【解决方案7】:
    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 变为假的其余时间旋转。哦,第二个循环将永远运行,因为它递增 i 而不是 j,哎呀...
    【解决方案8】:
    bool stop = false;
    for (int i = 0; (i < 1000) && !stop; i++)
    {
        for (int j = 0; (j < 1000) && !stop; j++)
        {
            if (condition)
                stop = true;
        }
    }
    

    【讨论】:

    • 解决方案仍然在 break 时将两个变量加一,这可能会导致问题
    • 可以设置“stop = true;”然后“打破;”。然后,在内部“for”循环结束后,执行“if (stop) break;”。
    • 我认为这是最优雅的非goto 解决方案。它不需要知道条件内ij 的限制。我喜欢将if (stop) break; 放在内部循环之后的想法,它本身可以在设置stop 之后break
    【解决方案9】:

    使用 LLVM 团队的明智建议:

    “把谓词循环变成谓词函数”

    见:

    http://llvm.org/docs/CodingStandards.html#turn-predicate-loops-into-predicate-functions

    【讨论】:

      【解决方案10】:

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

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

      【讨论】:

      • 请注意,如果条件依赖于j,则需要以某种方式存储条件的值才能使其仍然有效。
      • 你是对的,但是在 break 之后,j 的值没有改变,条件的值也是如此。
      • 这是一个损坏的解决方案,通常无效。要么 j 没有在其循环之外定义,要么 for (int i = 0; i &lt; 1000; i++) { for (int j = 0; j &lt; 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }总是在内循环的第一次迭代后跳出外循环。
      【解决方案11】:
      int i = 0, j= 0;
      
      for(i;i< 1000; i++){    
          for(j; j< 1000; j++){
              if(condition){
                  i = j = 1001;
                  break;
              }
          }
      }
      

      将打破两个循环。

      【讨论】:

        【解决方案12】:
        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);
        

        【讨论】:

          【解决方案13】:

          一种方法是将所有嵌套循环放入一个函数中,并从最内层循环返回,以防需要跳出所有循环。

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

          【讨论】:

            【解决方案14】:
            for(int i = 0; i < 1000; i++) {
               for(int j = 0; j < 1000; i++) {
                   if(condition) {
                        goto end;
               }
            } 
            
            end:
            

            【讨论】:

              【解决方案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;
                  }
                }
              }
              

              【讨论】:

                猜你喜欢
                • 2012-01-19
                • 1970-01-01
                • 2019-01-30
                • 2022-01-14
                • 2010-10-27
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多