2011-06-01 97 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是嵌套的循環,打破數。