2011-06-01 98 views
14

以下示例中终止所有嵌套循环的最佳方法是什么?一旦if语句为真,我想要终止语句的外层(使用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; 
     } 
    } 
} 
+1

请注意,您当前的“修复”仍然会延续中间循环,这可能会涉及更多的内部循环迭代。你需要'I = A = 10;' – 2011-06-01 12:24:30

+0

添加一个布尔并检查它? – Foresp 2011-06-01 13:54:04

+1

这怎么能不重复? – ripper234 2011-06-27 14:18:33

回答

32

我会重构这个方法,并只需要调用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(); 
+0

@Marc Gravell:谢谢马克!这段代码实际上是更大方法的一部分,所以我不能调用return。但是goto是个好主意! – Mirial 2011-06-01 12:24:37

+7

@Mirial - 选择你想要的块;在VS中,“提取方法” - 这就是我重构的意思。现在你有一个你可以从中返回的方法。基本上,你*可以*重构你的代码来做到这一点,你可能应该(这听起来像你的方法是过长的)。 – 2011-06-01 12:25:33

+1

我认为如果这个关键词在C#中不存在,那么年轻的开发人员在实现某些东西之前会考虑三次,并且正确地创建我。恕我直言,goto指令只能由编译器使用。 – 2011-06-01 12:25:58

1

你可以随时满足循环的期望:

如果(东西) B = 10

编辑:(看来你在帖子中包含此通过编辑)

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

满意(B,10)

然后它看起来更干净,但真的不需要。

3

为什么不这样做:

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

我认为你的意思是'&&!something'而不是'||。 something'。另外,有些东西可能是一种方法,您希望尽可能少执行。 – 2011-06-01 12:27:21

+0

,因为你不会完全爆发。你会回到A循环,所以也许重新插入B循环。所以你的例子只是简单地表明这不是一个好主意,因为它只是难以阅读和维护。 – Oliver 2011-06-01 12:28:17

+0

@奥利弗:完全同意,这将很难维护,而且非常难看。重构一种方法是最好的方法,恕我直言,正如马克上面所建议的那样。 – Yuck 2011-06-01 12:30:36

2

我会赞成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; 
    } 
14

假设你想从所有退出循环,你可以将它重构成更结构化的东西:

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

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

if (something)       
    break; 

在所有3个循环

2

如果这是方法的最后一项任务则条件为真时,你可以退货。 否则你必须让所有的值最大值

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

你总是可以利用的事实,那就是在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; 
      } 
     } 
    } 
} 
+0

这实际上是一个很好的解决方案 – nawfal 2012-02-11 07:38:37

1

个人而言,我会去与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 
2
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

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

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

,现在只有一个循环打出来的,而不是三个。

15

不要拍我,但是这实际上可能保证一个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"); 
+4

这实际上是合法使用'goto' IMO。 – Nobody 2011-06-01 15:35:03

+5

这正是goto是最佳解决方案的情况。它不会导致冗余条件检查的运行时间开销,并且比迄今为止发布的所有替代方法更重要且更容易理解。现在goto最糟糕的缺点是它的声誉不好。 – x4u 2011-06-01 18:07:29

+1

我拒绝听!射他! – VitalyB 2011-06-02 09:30:42

2

简单的办法就是嵌套循环重构与相关返回类型为一个单独的方法不管你想知道在那点:

在我的情况下,我会假设你想在这一点上的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; 
} 

如果您需要任何额外的状态传递给方法(边界,或一些位),只是将它们作为参数。

如果你想处理没有找到的第一个以不同的方式则像

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

将是一个备用。

至于使用变量名大写字母(尤其是单个字母的)一个侧面说明被认为是风格差在C#(和许多其他语言)

2

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

break n; 

n是嵌套的循环,打破数。