Làm thế nào để thoát ra khỏi các vòng lặp lồng nhau?


96

Nếu tôi sử dụng một breakcâu lệnh, nó sẽ chỉ phá vỡ vòng lặp bên trong và tôi cần sử dụng một số cờ để phá vỡ vòng lặp bên ngoài. Nhưng nếu có nhiều vòng lặp lồng nhau, mã sẽ trông không đẹp.

Có cách nào khác để phá vỡ tất cả các vòng lặp không? (Vui lòng không sử dụng 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

2
bạn có thể thử int i và int j trước khi vòng lặp bắt đầu và sau đó với điều kiện là 1001 vòng lặp sẽ không lặp lại lần tiếp theo.
Khurram Ijaz

Câu trả lời:


43

Sử dụng:

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

49
Hoạt động, nhưng xấu xí và không chung chung. Điều gì sẽ xảy ra nếu ai đó thay đổi giới hạn thành 2000 (giả sử mã dài hơn, vì vậy bạn không nhận thấy ngay lập tức)?
ugoren

1
@ugoren Nó cũng đơn giản như vậy. điều gì sẽ xảy ra nếu bạn sử dụng a const int count =1000 , trong Khởi tạo chung. hoặc như một #definemacro.
Laksith

4
Như @ugoren đã chỉ ra, đó không phải là một giải pháp chung. Vì đây là lần đầu tiên Google đưa ra câu hỏi này, sẽ rất tuyệt nếu giải pháp chung được chọn. Dù sao thì mọi người cũng quen với việc kiểm tra số 2.
BeeOnRope

1
Tôi đoán chỉ cần i = 1000?
Peter Wu

189

Không, đừng làm hỏng cuộc vui với a break. Đây là lần sử dụng hợp lệ cuối cùng còn lại của goto;)

Nếu không, bạn có thể sử dụng cờ để thoát ra khỏi các vòng lồng nhau sâu.

Một cách tiếp cận khác để thoát khỏi vòng lặp lồng nhau là tách cả hai vòng thành một hàm riêng biệt và quay trở lại từ hàm đó khi bạn muốn thoát.

Tóm tắt - để thoát ra khỏi các vòng lặp lồng nhau:

  1. sử dụng goto
  2. sử dụng cờ
  3. yếu tố ra các vòng lặp thành các lệnh gọi hàm riêng biệt

Không thể chống lại việc bao gồm xkcd ở đây :)

nhập mô tả hình ảnh ở đây

nguồn

Goto được coi là có hại nhưng nhiều người trong các ý kiến ​​cho rằng nó không cần thiết. Nếu được sử dụng một cách thận trọng, nó có thể là một công cụ tuyệt vời. Bất cứ thứ gì được sử dụng có chừng mực đều rất vui.


29
Goto là rõ ràng như bạn sẽ đến đây, vâng. Đặt biến thoát thành 1000 thậm chí còn khó hơn.
Correnos

3
Tôi muốn nói thêm rằng gotos không rõ ràng là xấu xa, chúng chỉ có thể được sử dụng cho điều ác. Tôi thấy rằng có khá nhiều trường hợp, ví dụ như trường hợp này, nơi chúng là giải pháp tốt nhất. "Không sử dụng gotos" là một khởi đầu tốt, nhưng tôi nghĩ rằng bước tiếp theo trong kỹ năng cho phép bạn "Không sử dụng gotos tầm xa".
Bắt

1
Tôi không đồng ý với điều này: "Tạo một hàm dẫn đến cộng / trừ con trỏ ngăn xếp theo cấp số nhân". Nếu có một hàm cục bộ (tĩnh) chỉ được gọi tại một điểm trong luồng chương trình, thì bất kỳ trình biên dịch nửa đàng hoàng nào cũng sẽ nội dòng nó và mã kết quả về cơ bản giống như với goto. Đây có thể là trường hợp tối ưu hóa dễ dàng nhất cho bất kỳ trình biên dịch nào.
DrV

1
Tái cấu trúc thường là giải pháp sạch nhất. Tuy nhiên, nếu bất kỳ biến bên ngoài vòng lặp nào bị thay đổi trong vòng lặp bên trong, mọi thứ sẽ trở nên phức tạp. Một khả năng là chuyển biến vào hàm bên trong bằng tham chiếu (con trỏ), nhưng điều này có thể gây nhầm lẫn cho việc tối ưu hóa trình biên dịch và tạo ra mã bổ sung không cần thiết. Một khả năng khác là làm cho các biến như vậy tĩnh ở cấp mô-đun, nhưng điều đó cũng không đẹp lắm. C rất tiếc là thiếu các hàm lồng nhau, vì chúng sẽ giải quyết vấn đề này - trừ khi bạn sẵn sàng tự ràng buộc mình với việc sử dụng gcc cung cấp tiện ích mở rộng.
DrV

1
+1. Và Lập trình có cấu trúc của Donald E. Knuth với đi tới Câu lệnh ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) là một bài viết thú vị để cân bằng Dijkstra's.
kmkaplan

40
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}

Giải pháp vẫn increments cả hai biến bằng một trong những ngày nghỉ mà có thể gây ra rắc rối
TheSola10

7
Người ta có thể đặt "stop = true;" và sau đó "break;". Sau đó, ngay sau khi kết thúc vòng lặp bên trong "for", hãy thực hiện "if (stop) break;".
Jeff Grigg,

34

Một cách là đặt tất cả các vòng lặp lồng nhau vào một hàm và trả về từ vòng lặp bên trong nhất nếu cần phải thoát ra khỏi tất cả các vòng lặp.

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

1
có vẻ như là giải pháp tốt nhất cho tôi
Lucatylesb

20

Tôi nghĩ gotosẽ giải quyết được vấn đề

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

end:
stmt2 

@chikuba Tôi đã nhận được câu trả lời từ cprogramming.com/tutorial/goto.html và câu trả lời của bạn không được đăng khi tôi đang làm điều tương tự, đó là lý do tại sao tôi không thấy bài đăng của bạn
Renjith KN

15

Bạn sẽ cần một biến boolean, nếu bạn muốn nó có thể đọc được:

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

Nếu bạn muốn nó khó đọc hơn, bạn có thể tham gia đánh giá boolean:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

Như một cách cuối cùng, bạn có thể làm mất hiệu lực của vòng lặp ban đầu:

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


4

Thận trọng: Câu trả lời này cho thấy một cấu trúc thực sự mù mờ.

Nếu bạn đang sử dụng GCC, hãy xem thư viện này . Giống như trong PHP, breakcó thể chấp nhận số lượng vòng lặp lồng nhau mà bạn muốn thoát. Bạn có thể viết một cái gì đó như thế này:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}

Và dưới mui xe nó thực sự được sử dụnggoto :)
iX3

@ iX3 Tôi có thể sử dụng trình hợp dịch nội tuyến và hướng dẫn jmp nếu điều đó hữu ích.
DaBler

@ DaBler, tôi không nhận ra bạn là tác giả của thư viện đó. Nhận xét của tôi không có nghĩa là phản hồi mà là lưu ý rằng thư viện này sử dụng cùng một phương pháp với câu trả lời được chấp nhận . Hy vọng rằng nhận xét của bạn chỉ là một trò đùa vì tôi nghĩ rằng sử dụng một tính năng ngôn ngữ (thậm chí goto) tốt hơn nhiều so với nội tuyến asm (máy cụ thể, dễ mắc lỗi hơn, khó đọc hơn, ...).
iX3 19/08/19

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

end:

3

Nếu bạn cần các giá trị của i và j, điều này sẽ hoạt động nhưng với hiệu suất kém hơn những giá trị khác

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

Lưu ý rằng nếu điều kiện phụ thuộc vào jthì giá trị của điều kiện cần được lưu trữ theo một cách nào đó để điều này vẫn hoạt động.
SuperBiasedMan

1
Bạn đúng nhưng sau break , giá trị của j không thay đổi và giá trị của điều kiện cũng vậy.
Ali Eren Çelik

Đây là một giải pháp bị hỏng và không có giá trị nói chung. Hoặc j không được xác định bên ngoài vòng lặp của nó hoặc for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }sẽ luôn thoát ra khỏi vòng lặp bên ngoài sau lần lặp đầu tiên của vòng lặp bên trong.
Chai T. Rex

-3
int i = 0, j= 0;

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition){
            i = j = 1001;
            break;
        }
    }
}

Sẽ phá vỡ cả hai vòng lặp.


-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}

Vì vậy, về cơ bản bạn đang nói rằng nó nên (1) thực hiện một loạt các lệnh gọi hàm bổ sung và sau đó (2) quay trong thời gian còn lại khi conditiontrở thành sai. Oh, và vòng lặp thứ hai sẽ chạy mãi mãi vì nó increments ithay vì j, tả ...
iX3

-4
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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.