【问题标题】:How to terminate outer loop in nested loops?如何在嵌套循环中终止外循环?
【发布时间】:2011-06-01 12:20:51
【问题描述】:

在下面的示例中,终止所有嵌套循环的最佳方法是什么。一旦 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;
        }
    }
}

【问题讨论】:

  • 请注意,您当前的“修复”仍会继续中间循环,这可能涉及内部循环的更多迭代。你需要I=A=10;
  • 这怎么能不重复?

标签: c# loops nested-loops


【解决方案1】:

我会将它重构为一个方法,并在需要时调用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();

【讨论】:

  • @Marc Gravell:谢谢马克!这段代码实际上是更大方法的一部分,所以我不能调用 return。但是 goto 是个好主意!
  • @Mirial - 选择你想要的块;在 VS 中,“提取方法”——这就是我所说的重构。现在你有一个方法可以return 使用。基本上,你可以重构你的代码来做到这一点,你可能应该(听起来你的方法太长了)。
  • 我认为如果 C# 中不存在这个关键字,那么年轻的开发人员在实现某些东西并正确创建 i 之前会三思而后行。恕我直言,goto 指令只能由编译器使用。
  • @Mirial 将代码重构为方法比使用 goto 更好。
  • “Marc Gravell 事实”即将到来 :)
【解决方案2】:

不要开枪,但这实际上可能需要转到:

 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");

【讨论】:

  • 这实际上是对gotoIMO 的合法使用。
  • 这正是 goto 是最佳解决方案的情况。它不会产生冗余条件检查的运行时开销,并且比迄今为止发布的所有替代方案更切中要害且更易于理解。现在 goto 最糟糕的缺点是它的坏名声。
  • 我拒绝听!开枪打死他!
【解决方案3】:

假设您想退出 所有 循环,您可以将其重构为更结构化的东西:

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

【讨论】:

    【解决方案4】:

    如果循环体不产生副作用,而只是寻找“某事”为真的第一个值,那么首先通过消除所有循环来解决问题。

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

    现在只有一个循环可以跳出,而不是三个。

    【讨论】:

      【解决方案5】:

      为什么不这样做:

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

      【讨论】:

      • 我认为您的意思是 &amp;&amp; !something 而不是 || something。此外,某些东西很可能是一种方法,您希望尽可能少地执行。
      • 因为你不会完全爆发。您会退回到 A 循环,因此可能会重新插入 B 循环。所以你的例子只是表明这不是一个好主意,因为它很难阅读和维护。
      • @Oliver:完全同意,这将很难维护,而且非常难看。恕我直言,重构为方法是最好的方法,正如 Marc 上面所建议的那样。
      • 我喜欢这个:) --- 不过可能想要&amp;&amp; !something。我不认为 I = 10 是必要的
      • 避免 goto 的体操比只使用 goto 更糟糕。这是 goto 的有效用途...人们混淆了“goto 被认为有害”的问题,并认为它总是有害...如果在非常具体和有限的情况下使用,在了解并考虑所有其他的选择,没关系
      【解决方案6】:

      您总是可以利用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;
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案7】:

        我会倾向于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;
            }
        

        【讨论】:

          【解决方案8】:

          如果这是方法中的最终任务,那么您可以在条件为真时返回。 否则你必须将所有值设为最大值

          if (something)              
              {
                  I=10;   
                  B=10;
                  A=10;
                  break;
              }
          

          【讨论】:

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

            一个非常原始的解决方案。

            【讨论】:

              【解决方案10】:

              简单的解决方案是将嵌套循环重构为一个单独的方法,相关的返回类型是您当时想知道的任何内容:

              在我的情况下,我会假设您当时想要 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;
              }
              

              如果您需要将任何其他状态传递给方法(用于边界或某物位),只需将它们作为参数传递。

              如果你想以不同的方式处理未能找到第一个的问题,那么就像

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

              将是替代品。

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

              【讨论】:

                【解决方案11】:

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

                break n;
                

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

                【讨论】:

                  【解决方案12】:

                  你总能满足循环的期望:

                  如果(某事) B = 10

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

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

                  满足(B,10)

                  那么它看起来更干净,但实际上不需要。

                  【讨论】:

                    【解决方案13】:

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

                    if (something)                         
                       break; 
                    

                    在所有 3 个循环中

                    【讨论】:

                      【解决方案14】:

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

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2014-04-12
                        • 2018-07-15
                        • 2020-01-03
                        • 1970-01-01
                        • 2012-10-15
                        • 1970-01-01
                        • 1970-01-01
                        • 2012-05-20
                        相关资源
                        最近更新 更多